|
| 1 | +.. _ref_quickstart_dynamic: |
| 2 | + |
| 3 | +=============== |
| 4 | +Dynamic Queries |
| 5 | +=============== |
| 6 | + |
| 7 | +.. edb:split-section:: |
| 8 | +
|
| 9 | + Maybe we only want to update one side of an existing card, or just edit the description of a deck. One approach is writing a very complicated single query that tries to handle all of the dynamic cases. Another approach is to build the query dynamically in the application code. This has the benefit of often being better for performance, and it's easier to understand and maintain. We provide another very powerful code generator, our TypeScript query builder, that allows you to build queries dynamically in the application code, while giving you strict type safety. |
| 10 | + |
| 11 | + First, we will generate the query builder. This will generate a module in our ``dbschema`` directory called ``edgeql-js``, which we can import in our route and use to build a dynamic query. |
| 12 | + |
| 13 | + .. code-block:: sh |
| 14 | +
|
| 15 | + $ npx @gel/generate edgeql-js |
| 16 | +
|
| 17 | +
|
| 18 | +.. edb:split-section:: |
| 19 | +
|
| 20 | + Now let's use the query builder in a new route for updating a deck's ``name`` and/or ``description``. We will treat the request body as a partial update, and only update the fields that are provided. Since the description is optional, we will use a nullable string for the type, so you can "unset" the description by passing in ``null``. |
| 21 | + |
| 22 | + .. code-block:: typescript-diff |
| 23 | + :caption: app/api/deck/[id]/route.ts |
| 24 | +
|
| 25 | + import { NextRequest, NextResponse } from "next/server"; |
| 26 | + import { getAuthenticatedClient } from "@/lib/gel"; |
| 27 | + + import e from "@/dbschema/edgeql-js"; |
| 28 | +
|
| 29 | + import { getDeck } from "./get-deck.query"; |
| 30 | +
|
| 31 | + interface GetDeckSuccessResponse { |
| 32 | + id: string; |
| 33 | + name: string; |
| 34 | + description: string | null; |
| 35 | + creator: { |
| 36 | + id: string; |
| 37 | + name: string; |
| 38 | + } | null; |
| 39 | + cards: { |
| 40 | + id: string; |
| 41 | + front: string; |
| 42 | + back: string; |
| 43 | + }[]; |
| 44 | + } |
| 45 | +
|
| 46 | + interface GetDeckErrorResponse { |
| 47 | + error: string; |
| 48 | + } |
| 49 | +
|
| 50 | + type GetDeckResponse = GetDeckSuccessResponse | GetDeckErrorResponse; |
| 51 | +
|
| 52 | + export async function GET( |
| 53 | + req: NextRequest, |
| 54 | + { params }: { params: Promise<{ id: string }> } |
| 55 | + ): Promise<NextResponse<GetDeckResponse>> { |
| 56 | + const client = getAuthenticatedClient(req); |
| 57 | +
|
| 58 | + if (!client) { |
| 59 | + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); |
| 60 | + } |
| 61 | +
|
| 62 | + const { id: deckId } = await params; |
| 63 | + const deck = await getDeck(client, { deckId }); |
| 64 | +
|
| 65 | + if (!deck) { |
| 66 | + return NextResponse.json( |
| 67 | + { error: `Deck (${deckId}) not found` }, |
| 68 | + { status: 404 } |
| 69 | + ); |
| 70 | + } |
| 71 | +
|
| 72 | + return NextResponse.json(deck); |
| 73 | + } |
| 74 | +
|
| 75 | + + interface UpdateDeckBody { |
| 76 | + + name?: string; |
| 77 | + + description?: string | null; |
| 78 | + + } |
| 79 | + + |
| 80 | + + interface UpdateDeckSuccessResponse { |
| 81 | + + id: string; |
| 82 | + + } |
| 83 | + + |
| 84 | + + interface UpdateDeckErrorResponse { |
| 85 | + + error: string; |
| 86 | + + } |
| 87 | + + |
| 88 | + + type UpdateDeckResponse = UpdateDeckSuccessResponse | UpdateDeckErrorResponse; |
| 89 | + + |
| 90 | + + export async function PATCH( |
| 91 | + + req: NextRequest, |
| 92 | + + { params }: { params: Promise<{ id: string }> } |
| 93 | + + ): Promise<NextResponse<UpdateDeckResponse>> { |
| 94 | + + const client = getAuthenticatedClient(req); |
| 95 | + + |
| 96 | + + if (!client) { |
| 97 | + + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); |
| 98 | + + } |
| 99 | + + |
| 100 | + + const { id: deckId } = await params; |
| 101 | + + const body = (await req.json()) as UpdateDeckBody; |
| 102 | + + |
| 103 | + + const nameSet = body.name !== undefined ? { name: body.name } : {}; |
| 104 | + + const descriptionSet = |
| 105 | + + body.description !== undefined ? { description: body.description } : {}; |
| 106 | + + |
| 107 | + + const updated = await e |
| 108 | + + .update(e.Deck, (deck) => ({ |
| 109 | + + filter_single: e.op(deck.id, "=", deckId), |
| 110 | + + set: { |
| 111 | + + ...nameSet, |
| 112 | + + ...descriptionSet, |
| 113 | + + }, |
| 114 | + + })) |
| 115 | + + .run(client); |
| 116 | + + |
| 117 | + + if (!updated) { |
| 118 | + + return NextResponse.json( |
| 119 | + + { error: `Deck (${deckId}) not found` }, |
| 120 | + + { status: 404 } |
| 121 | + + ); |
| 122 | + + } |
| 123 | + + |
| 124 | + + return NextResponse.json(updated); |
| 125 | + + } |
0 commit comments