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.