Getting started with Samveys
Dari docs/guide/getting-started.md di repository framework.
Halaman ini belum diterjemahkan. Kamu sedang membaca versi bahasa Inggris.
This tutorial builds a small HTTP API with one validated POST route and a node:test test using the HTTP kernel’s Fetch API. It uses only packages and value exports present in this repository. playground/ is a scratch area you create yourself; it is not part of the repository. The snippets are type-checked against the package exports, and the test in step 4 runs in CI (tested).
1. Prepare a workspace example
From the monorepo root, create a local tutorial directory. Keep it inside this repository so TypeScript resolves the workspace packages. For a standalone project, use svs new instead (tutorial).
mkdir -p playground/getting-startedThe workspace already defines Node.js ≥ 26.13.0, ESM, and direct execution of .ts files. This guide assumes the monorepo dependencies are installed and its package links are available. Do not fetch these internal-only packages from the public npm registry.
2. Define the application and route
Create playground/getting-started/app.ts. The app uses the HTTP plugin, a controller, and a Standard Schema validator. The created method returns HTTP 201 and a JSON response.
import { createApp } from '@samveys/core'
import { defineController, createHttpServer, http, post } from '@samveys/http'
import { schema } from '@samveys/validator'
const createMessage = schema.object({
text: schema.string().trim().min(1),
})
export const messages = defineController('messages', {
prefix: '/messages',
routes: {
create: post('/', { body: createMessage }, ({ body, response }) =>
response.created({ id: 'm-1', text: body.text }),
),
},
})
export const app = createApp({
plugins: [http()],
environment: 'test',
processEnv: {},
})
export async function makeKernel() {
await app.run()
const router = app.container.make('http.router')
router.controller(messages)
return createHttpServer(app)
}createHttpServer() returns both the server and its kernel. The tutorial test can use kernel.fetch() without opening a network port. A real entrypoint can call listen() on the returned server.
3. Test the validated POST route
Create playground/getting-started/app.test.ts. node:test runs this test directly with the repository’s Node 26 TypeScript runtime. A malformed body is rejected before the route handler runs and gets a Problem Details response.
import assert from 'node:assert/strict'
import { after, test } from 'node:test'
import { app, makeKernel } from './app.ts'
const server = await makeKernel()
after(async () => {
await server.close()
await app.shutdown()
})
test('creates a validated message', async () => {
const response = await server.kernel.fetch(
new Request('http://localhost/messages', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ text: ' hello ' }),
}),
)
assert.equal(response.status, 201)
assert.deepEqual(await response.json(), { id: 'm-1', text: 'hello' })
})
test('rejects invalid input with Problem Details', async () => {
const response = await server.kernel.fetch(
new Request('http://localhost/messages', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ text: '' }),
}),
)
assert.equal(response.status, 422)
assert.equal(response.headers.get('content-type'), 'application/problem+json')
const problem = await response.json() as { code: string; errors: { pointer: string }[] }
assert.equal(problem.code, 'SVS_E042')
assert.equal(problem.errors[0]?.pointer, '#/body/text')
})Run the test from the monorepo root with Node’s test runner:
node --test playground/getting-started/app.test.ts4. The same test with @samveys/testing
createTestApp() from @samveys/testing removes the setup above: it boots a fresh app for each test, gives it an in-process HTTP client with assertions, and shuts it down at the end of the block through await using. Add "@samveys/testing": "workspace:*" to the dev dependencies of the package that holds the test.
import { test } from 'node:test'
import { http } from '@samveys/http'
import { createTestApp } from '@samveys/testing'
import { messages } from './app.ts'
test('creates a validated message', async () => {
await using testApp = await createTestApp({
plugins: [http()],
routes: (router) => router.controller(messages),
})
;(await testApp.http.post('/messages', { json: { text: ' hello ' } }))
.assertStatus(201)
.assertJson({ id: 'm-1', text: 'hello' })
;(await testApp.http.post('/messages', { json: { text: '' } })).assertProblem(422, 'SVS_E042')
})See packages/testing/README.md for fake time, event and hook recording, and swap().
5. Start the server
Create playground/getting-started/bin/server.ts to listen on the configured Node driver. The server closes gracefully when the app shuts down.
import { app, makeKernel } from '../app.ts'
const server = await makeKernel()
await server.listen()
app.handleSignals()The router now serves POST /messages. Invalid request bodies return 422 with an issue pointer; valid bodies are trimmed and receive a 201 JSON response.
Langkah yang sama dalam satu prompt untuk asisten coding (Claude Code, Cursor, Copilot…). Tempel ke asisten di folder kosong; ia menjalankan tiap langkah dan berhenti di error pertama. Prompt-nya sengaja berbahasa Inggris.
Follow the Samveys "Getting started" guide in this repository (the Samveys framework monorepo).
Work in a new folder, playground/getting-started/, inside the monorepo root so TypeScript resolves the workspace packages.
Do not fetch @samveys/* from the public npm registry; the workspace links them. Node is 26.13.0+ and runs .ts files directly.
1. Create playground/getting-started/.
2. Create playground/getting-started/app.ts:
- A Standard Schema body `schema.object({ text: schema.string().trim().min(1) })` from @samveys/validator.
- A controller `messages` (defineController from @samveys/http) with prefix '/messages' and a `create` route:
`post('/', { body }, ({ body, response }) => response.created({ id: 'm-1', text: body.text }))`.
- `export const app = createApp({ plugins: [http()], environment: 'test', processEnv: {} })` from @samveys/core.
- `export async function makeKernel()`: await app.run(), register the controller on app.container.make('http.router'),
and return createHttpServer(app).
3. Create playground/getting-started/app.test.ts with node:test, using server.kernel.fetch() (no network port):
- POST { text: ' hello ' } → 201 with { id: 'm-1', text: 'hello' }.
- POST { text: '' } → 422, content-type application/problem+json, code 'SVS_E042', errors[0].pointer '#/body/text'.
- Close the server and call app.shutdown() in after().
Run it: `node --test playground/getting-started/app.test.ts`
4. Write the same test with createTestApp() from @samveys/testing (`await using`, testApp.http.post(...).assertStatus(201)
.assertJson(...), and .assertProblem(422, 'SVS_E042') for the bad body). Add "@samveys/testing": "workspace:*" to devDependencies.
5. Create playground/getting-started/bin/server.ts: `const server = await makeKernel(); await server.listen(); app.handleSignals()`.
Run the tests after steps 3 and 4 and show me the output. Stop and show me the error if anything fails.