Created
October 30, 2024 15:47
-
-
Save noook/dcae2eef50a9e9181c1089791dc61bfd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Import h3 as npm dependency | |
| import { createApp, createRouter, defineEventHandler, getRouterParam, sendError, createError, readBody, readValidatedBody, setResponseStatus, sendNoContent, getQuery, getValidatedQuery, H3Event, getRequestHeader } from "h3"; | |
| import { z } from 'zod' | |
| import type { Prettify } from './utils' | |
| // Create an app instance | |
| export const app = createApp(); | |
| // Create a new router and register it in app | |
| const router = createRouter(); | |
| app.use(router); | |
| interface Identifiable { | |
| id: number | |
| } | |
| // interface IBook extends Identifiable { | |
| // title: string | |
| // author: string | |
| // releaseYear: number | |
| // genres: string[] | |
| // rating: number | |
| // } | |
| const bookSchema = z.object({ | |
| title: z.string(), | |
| author: z.string(), | |
| releaseYear: z.number(), | |
| // genres: z.array(z.string()) | |
| genres: z.string().array() | |
| .min(1, 'Le livre doit avoir au moins un genre'), | |
| rating: z.number() | |
| .min(1, 'Rating doit être supérieur à 1') | |
| .max(5, 'Rating doit être inférieur à 5'), | |
| }) | |
| // Intersection des deux types | |
| type Book = Identifiable & z.infer<typeof bookSchema> | |
| const books: Book[] = [ | |
| { id: 1, title: 'The Great Gatsby', author: 'F. Scott Fitzgerald', releaseYear: 1925, genres: ['novel'], rating: 5 }, | |
| { id: 2, title: 'To Kill a Mockingbird', author: 'Harper Lee', releaseYear: 1960, genres: ['novel'], rating: 5 }, | |
| { id: 3, title: '1984', author: 'George Orwell', releaseYear: 1949, genres: ['dystopian', 'science fiction'], rating: 5 }, | |
| { id: 4, title: 'Pride and Prejudice', author: 'Jane Austen', releaseYear: 1813, genres: ['romance', 'novel'], rating: 4 }, | |
| { id: 5, title: 'The Catcher in the Rye', author: 'J.D. Salinger', releaseYear: 1951, genres: ['novel'], rating: 4 }, | |
| { id: 6, title: 'The Hobbit', author: 'J.R.R. Tolkien', releaseYear: 1937, genres: ['fantasy'], rating: 5 }, | |
| ] | |
| function serializeText(book: Book): string { | |
| return `${book.title} by ${book.author}, released in ${book.releaseYear}` | |
| } | |
| function getRequestedContentType(event: H3Event) { | |
| const mimeType = getRequestHeader(event, 'Accept') | |
| if (mimeType === 'text/plain') { | |
| return 'text/plain' | |
| } | |
| return 'application/json' | |
| } | |
| // Add a new route that matches GET requests to / path | |
| router.get( | |
| "/", | |
| defineEventHandler((event) => { | |
| return { message: "⚡️ Tadaa Welcome home" }; | |
| }), | |
| ) | |
| const paginationQuerySchema = z.object({ | |
| page: z.coerce.number() | |
| .min(1, 'Page doit être supérieur à 1') | |
| .default(1), | |
| limit: z.coerce.number() | |
| .min(2) | |
| .max(10) | |
| .default(3), | |
| }) | |
| // ?page=&1page=2 | |
| // => { page: ["1", "2"] } | |
| // ?page=1 | |
| // => { page: "1" } | |
| router.get( | |
| '/books', | |
| defineEventHandler(async event => { | |
| const { page, limit } = await getValidatedQuery(event, paginationQuerySchema.parse) | |
| const maxPage = Math.ceil(books.length / limit) | |
| if (page > maxPage) { | |
| return sendError(event, createError({ | |
| statusCode: 400, | |
| statusMessage: `Page ${page} is out of range (max: ${maxPage})` | |
| })) | |
| } | |
| const offset = (page - 1) * limit | |
| const booksSlice = books.slice(offset, page * limit) | |
| if (getRequestedContentType(event) === 'text/plain') { | |
| return booksSlice.map(book => serializeText(book)).join('\n') | |
| } | |
| return { | |
| data: booksSlice, | |
| page, | |
| limit, | |
| maxPage, | |
| total: books.length, | |
| } | |
| }) | |
| ) | |
| router.get( | |
| '/books/:id', | |
| defineEventHandler(event => { | |
| const id = getRouterParam(event, 'id') | |
| if (!id) { | |
| return sendError(event, createError({ | |
| statusCode: 400, | |
| message: 'Missing id parameter' | |
| })) | |
| } | |
| const idNumber = Number(id) | |
| const book = books.find(book => book.id === idNumber) | |
| if (!book) { | |
| return sendError(event, createError({ | |
| statusCode: 404, | |
| message: 'Book not found' | |
| })) | |
| } | |
| if (getRequestedContentType(event) === 'text/plain') { | |
| return serializeText(book) | |
| } | |
| return book | |
| }), | |
| ) | |
| router.post( | |
| '/books', | |
| defineEventHandler(async event => { | |
| // const body = await readValidatedBody(event, data => bookSchema.parse(data)) | |
| // 400 Bad request = body not compliant with schema | |
| const body = await readValidatedBody(event, bookSchema.parse) | |
| const nextId = books.at(-1)!.id + 1 | |
| const newBook = { id: nextId, ...body } | |
| books.push(newBook) | |
| setResponseStatus(event, 201) | |
| if (getRequestedContentType(event) === 'text/plain') { | |
| return serializeText(newBook) | |
| } | |
| return newBook | |
| }), | |
| ) | |
| router.put( | |
| '/books/:id', | |
| defineEventHandler(async event => { | |
| const id = getRouterParam(event, 'id') | |
| if (!id) { | |
| return sendError(event, createError({ | |
| statusCode: 400, | |
| message: 'Missing id parameter' | |
| })) | |
| } | |
| const idNumber = Number(id) | |
| const bookIdx = books.findIndex(book => book.id === idNumber) | |
| if (bookIdx === -1) { | |
| return sendError(event, createError({ | |
| statusCode: 404, | |
| message: 'Book not found' | |
| })) | |
| } | |
| const body = await readValidatedBody(event, bookSchema.parse) | |
| const updatedBook = { id: idNumber, ...body } | |
| books[bookIdx] = updatedBook | |
| if (getRequestedContentType(event) === 'text/plain') { | |
| return serializeText(updatedBook) | |
| } | |
| return updatedBook | |
| }), | |
| ) | |
| .delete( | |
| '/books/:id', | |
| defineEventHandler(event => { | |
| const id = getRouterParam(event, 'id') | |
| if (!id) { | |
| return sendError(event, createError({ | |
| statusCode: 400, | |
| message: 'Missing id parameter' | |
| })) | |
| } | |
| const idNumber = Number(id) | |
| const bookIdx = books.findIndex(book => book.id === idNumber) | |
| if (bookIdx === -1) { | |
| return sendError(event, createError({ | |
| statusCode: 404, | |
| message: 'Book not found' | |
| })) | |
| } | |
| books.splice(bookIdx, 1) | |
| // setResponseStatus(event, 204) | |
| // sendNoContent(event) | |
| // return null | |
| return sendNoContent(event) | |
| }), | |
| ) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment