1+ import { BaseSQLiteDatabase } from "drizzle-orm/sqlite-core" ;
2+ import { sql } from "drizzle-orm" ;
13import { Middleware } from "koa" ;
24import Router from "@koa/router" ;
35import compose from "koa-compose" ;
@@ -7,10 +9,14 @@ import { debug } from "../debug";
79import { createBenchmark } from "@latticexyz/common" ;
810import { compress } from "../koa-middleware/compress" ;
911import { getTablesWithRecords } from "./getTablesWithRecords" ;
10- import { BaseSQLiteDatabase } from "drizzle-orm/sqlite-core" ;
1112
12- // eslint-disable-next-line @typescript-eslint/no-explicit-any
13- export function apiRoutes ( database : BaseSQLiteDatabase < "sync" , any > ) : Middleware {
13+ type Props = {
14+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
15+ database : BaseSQLiteDatabase < "sync" , any > ;
16+ enableUnsafeQueryApi ?: boolean ;
17+ } ;
18+
19+ export function apiRoutes ( { database, enableUnsafeQueryApi = false } : Props ) : Middleware {
1420 const router = new Router ( ) ;
1521
1622 router . get ( "/api/logs" , compress ( ) , async ( ctx ) => {
@@ -44,5 +50,50 @@ export function apiRoutes(database: BaseSQLiteDatabase<"sync", any>): Middleware
4450 }
4551 } ) ;
4652
53+ router . post ( "/q" , async ( ctx ) => {
54+ if ( ! enableUnsafeQueryApi ) {
55+ ctx . status = 404 ;
56+ ctx . body = JSON . stringify ( { error : "Query endpoint is not enabled" } ) ;
57+ return ;
58+ }
59+
60+ try {
61+ const queries = Array . isArray ( ctx . request . body ) ? ctx . request . body : [ ] ;
62+ if ( queries . length === 0 ) {
63+ ctx . status = 400 ;
64+ ctx . body = JSON . stringify ( { error : "No queries provided" } ) ;
65+ return ;
66+ }
67+
68+ const result = [ ] ;
69+ for ( const { query } of queries ) {
70+ const data = database . all ( sql . raw ( query ) ) as Record < string , unknown > [ ] ;
71+ if ( ! data || ! Array . isArray ( data ) ) {
72+ throw new Error ( "Invalid query result" ) ;
73+ }
74+
75+ if ( data . length === 0 ) {
76+ result . push ( [ ] ) ;
77+ continue ;
78+ }
79+
80+ if ( ! data [ 0 ] ) {
81+ throw new Error ( "Invalid row data" ) ;
82+ }
83+
84+ const columns = Object . keys ( data [ 0 ] ) . map ( ( key ) => key . replaceAll ( "_" , "" ) . toLowerCase ( ) ) ;
85+ const rows = data . map ( ( row ) => Object . values ( row ) . map ( ( value ) => value ?. toString ( ) ?? "" ) ) ;
86+ result . push ( [ columns , ...rows ] ) ;
87+ }
88+
89+ ctx . status = 200 ;
90+ ctx . body = JSON . stringify ( { result } ) ;
91+ } catch ( error ) {
92+ const errorMessage = error instanceof Error ? error . message : "An unknown error occurred" ;
93+ ctx . status = 400 ;
94+ ctx . body = JSON . stringify ( { error : errorMessage } ) ;
95+ }
96+ } ) ;
97+
4798 return compose ( [ router . routes ( ) , router . allowedMethods ( ) ] ) as Middleware ;
4899}
0 commit comments