Skip to content

Commit cb77081

Browse files
committed
refactor: with-fastify example
1 parent 186f97a commit cb77081

File tree

10 files changed

+313
-69
lines changed

10 files changed

+313
-69
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { Post, User } from '../../dto/users.dto.js'
2+
import {
3+
userIdAndPostIdParamsSchema,
4+
userIdParamsSchema
5+
} from './posts.schema.js'
6+
import type { UserService } from '../users/users.service.js'
7+
import type { FastifyInstance } from 'fastify'
8+
9+
export function postsController(
10+
fastify: FastifyInstance,
11+
service: UserService
12+
): void {
13+
fastify.get('/', () => {
14+
return service.userPosts
15+
})
16+
17+
fastify.get<{ Params: { userId: number } }>(
18+
'/:userId',
19+
{ schema: userIdParamsSchema },
20+
(request, reply) => {
21+
const user = service.findUserById(request.params.userId)
22+
if (!user) return reply.status(404).send('User not found')
23+
return user.posts
24+
}
25+
)
26+
27+
fastify.get<{ Params: { userId: number; postId: number } }>(
28+
'/:userId/:postId',
29+
{ schema: userIdAndPostIdParamsSchema },
30+
(request, reply) => {
31+
const { userId, postId } = request.params
32+
const user = service.findUserById(userId)
33+
if (!user) return reply.status(404).send('User not found')
34+
35+
const post = user.findPost(postId)
36+
if (!post) return reply.status(404).send('Post not found')
37+
return post
38+
}
39+
)
40+
41+
fastify.post<{ Body: Post; Params: User }>(
42+
'/:userId',
43+
{ schema: userIdParamsSchema },
44+
async (request, reply) => {
45+
const user = service.findUserById(request.params.userId)
46+
if (!user) return reply.status(404).send('User not found')
47+
48+
const post = new Post(
49+
user.postsLastId + 1,
50+
request.body.subject,
51+
new Date()
52+
)
53+
user.addPost(post)
54+
await service.write()
55+
return post
56+
}
57+
)
58+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export const userIdParamsSchema = {
2+
params: {
3+
userId: {
4+
$ref: 'User#/properties/userId'
5+
}
6+
}
7+
}
8+
9+
export const userIdAndPostIdParamsSchema = {
10+
params: {
11+
userId: {
12+
$ref: 'User#/properties/userId'
13+
},
14+
postId: {
15+
$ref: 'Post#/properties/postId'
16+
}
17+
}
18+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { User } from '../../dto/users.dto.js'
2+
import { userIdParamsSchema } from '../posts/posts.schema.js'
3+
import { userBodySchema, userResponseSchema } from './users.schema.js'
4+
import type { UserService } from './users.service.js'
5+
import type { FastifyInstance } from 'fastify'
6+
7+
export function userController(
8+
fastify: FastifyInstance,
9+
service: UserService
10+
): void {
11+
fastify.get('/', () => {
12+
return service.users
13+
})
14+
15+
fastify.get<{ Params: User }>(
16+
'/:userId',
17+
{
18+
schema: userIdParamsSchema
19+
},
20+
(request, reply) => {
21+
const user = service.findUserById(request.params.userId)
22+
if (!user) return reply.status(404).send('User not found')
23+
24+
return {
25+
id: user.userId,
26+
username: user.username,
27+
age: user.age
28+
}
29+
}
30+
)
31+
32+
fastify.post<{ Body: User }>(
33+
'/',
34+
{ schema: userBodySchema },
35+
async (request, reply) => {
36+
const { username, age } = request.body
37+
const newUser = service.addUser(username, age)
38+
if (!newUser) return reply.status(400).send('Username exist')
39+
return newUser
40+
}
41+
)
42+
43+
fastify.delete<{ Params: User }>(
44+
'/:userId',
45+
{ schema: userResponseSchema },
46+
async (request, reply) => {
47+
const user = await service.deleteUser(request.params.userId)
48+
if (!user) return reply.status(404).send('User not found')
49+
return user
50+
}
51+
)
52+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export const userBodySchema = {
2+
body: { $ref: 'User' }
3+
}
4+
5+
export const userResponseSchema = {
6+
response: {
7+
200: { $ref: 'User' }
8+
}
9+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { User, Users } from '../../dto/users.dto.js'
2+
import type { Steno } from '@stenodb/fastify'
3+
import type { FastifyInstance } from 'fastify'
4+
5+
export class UserService {
6+
#users: Steno.NodeProvider<Users>
7+
8+
constructor(private readonly fastify: FastifyInstance) {
9+
this.#users = this.fastify.steno.get<Users>('users')!
10+
}
11+
12+
get users(): User[] {
13+
return this.#users.data!.users
14+
}
15+
16+
get userPosts() {
17+
return this.users.map((user) => {
18+
return {
19+
id: user.userId,
20+
posts: user.posts
21+
}
22+
})
23+
}
24+
25+
get userLastId(): number {
26+
return this.users.at(-1)!.userId
27+
}
28+
29+
async write(): Promise<void> {
30+
await this.#users.write()
31+
}
32+
33+
async addUser(username: string, age: number): Promise<User | null> {
34+
if (this.findUserByName(username)) return null
35+
36+
const user = new User(this.userLastId + 1, username.toLowerCase(), age)
37+
this.users.push(user)
38+
await this.write()
39+
40+
return user
41+
}
42+
43+
findUserById(userId: number): User | undefined {
44+
return this.users.find((user) => user.userId === userId)
45+
}
46+
47+
findUserByName(userName: string): User | undefined {
48+
return this.users.find((user) => user.username == userName)
49+
}
50+
51+
async deleteUser(userId: number): Promise<User | null> {
52+
const user = this.findUserById(userId)
53+
if (user) {
54+
this.#users.data!.users = this.users.filter(
55+
(user) => user.userId !== userId
56+
)
57+
await this.write()
58+
return user
59+
}
60+
61+
return null
62+
}
63+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { AsyncAdapter } from '@stenodb/fastify'
2+
import { Exclude, Type } from 'class-transformer'
3+
import { IsNumber, IsOptional, IsString, Length } from 'class-validator'
4+
5+
export class Users {
6+
@Type(() => User)
7+
users: User[]
8+
9+
constructor(...users: User[]) {
10+
this.users = users
11+
}
12+
}
13+
14+
export class User {
15+
@Exclude({ toPlainOnly: true })
16+
@IsNumber()
17+
userId: number
18+
19+
@IsString()
20+
@Length(3, 20)
21+
username: string
22+
23+
@IsNumber()
24+
age: number
25+
26+
@Exclude({ toPlainOnly: true })
27+
@Type(() => Post)
28+
posts: Post[]
29+
30+
constructor(userId: number, username: string, age: number, ...posts: Post[]) {
31+
this.userId = userId
32+
this.username = username
33+
this.age = age
34+
this.posts = posts
35+
}
36+
37+
findPost(postId: number): Post | undefined {
38+
return this.posts.find((post) => post.postId === postId)
39+
}
40+
41+
addPost(post: Post) {
42+
this.posts.push(post)
43+
}
44+
45+
get postsLastId(): number {
46+
return this.posts.at(-1)!.postId
47+
}
48+
}
49+
50+
export class Post {
51+
@Exclude({ toPlainOnly: true })
52+
@IsNumber()
53+
postId: number
54+
55+
@IsString()
56+
@Length(1, 128)
57+
subject: string
58+
59+
@Exclude({ toPlainOnly: true })
60+
@Type(() => Date)
61+
createdAt: Date
62+
63+
constructor(postId: number, subject: string, createdAt: Date) {
64+
this.postId = postId
65+
this.subject = subject
66+
this.createdAt = createdAt
67+
}
68+
}
69+
70+
const initialData = new Users(
71+
new User(1, 'john', 18, new Post(1, 'Lorem ipsum', new Date())),
72+
new User(2, 'alice', 23)
73+
)
74+
75+
export const userEntities = [User, Post]
76+
77+
export const users = new AsyncAdapter('users', Users, initialData)

examples/with-fastify/src/entities.ts

Lines changed: 0 additions & 48 deletions
This file was deleted.

examples/with-fastify/src/index.ts

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,44 @@
11
import 'reflect-metadata'
22
import { dirname, resolve } from 'node:path'
33
import { fileURLToPath } from 'node:url'
4-
import { AsyncAdapter, FastifySteno } from '@stenodb/fastify'
4+
import { FastifySteno } from '@stenodb/fastify'
55
import Fastify from 'fastify'
6-
import { Post, User, Users } from './entities.js'
6+
import { postsController } from './api/posts/posts.controller.js'
7+
import { userController } from './api/users/users.controller.js'
8+
import { UserService } from './api/users/users.service.js'
9+
import { userEntities, users } from './dto/users.dto.js'
710

811
const fastify = Fastify()
912

1013
fastify.register(FastifySteno, {
1114
path: resolve(dirname(fileURLToPath(import.meta.url)), '..', 'db'),
12-
entities: [User, Post],
13-
adapters: [new AsyncAdapter('users', Users, new Users(new User('John', 18)))]
15+
entities: [...userEntities],
16+
adapters: [users]
1417
})
1518

16-
fastify.get('/', () => {
17-
const users = fastify.steno.get<Users>('users')!
18-
return users.data
19-
})
19+
fastify.register(
20+
(instance, _, done) => {
21+
const userService = new UserService(fastify)
22+
23+
instance.register(
24+
(instance, _, done) => {
25+
userController(instance, userService)
26+
done()
27+
},
28+
{ prefix: '/users' }
29+
)
30+
31+
instance.register(
32+
(instance, _, done) => {
33+
postsController(instance, userService)
34+
done()
35+
},
36+
{ prefix: '/posts' }
37+
)
2038

21-
fastify.post<{ Body: User }>(
22-
'/',
23-
{ schema: { body: { $ref: 'User' } } },
24-
async (req) => {
25-
const users = fastify.steno.get<Users>('users')!
26-
users.data!.users.push(req.body)
27-
await users.write()
28-
return users.data
29-
}
39+
done()
40+
},
41+
{ prefix: '/api' }
3042
)
3143

3244
fastify.get('/schemas', () => {

0 commit comments

Comments
 (0)