diff --git a/README.md b/README.md
index 86b3745..bb1bc8e 100644
--- a/README.md
+++ b/README.md
@@ -222,12 +222,32 @@ You can request a `database_dump.sql` file that exports your database schema and
-curl --location 'https://starbasedb.YOUR-ID-HERE.workers.dev/dump' \
+curl --location 'https://starbasedb.YOUR-ID-HERE.workers.dev/export/dump' \
--header 'Authorization: Bearer ABC123'
--output database_dump.sql
+JSON Data Export
+
+
+curl
+--location 'https://starbasedb.YOUR-ID-HERE.workers.dev/export/json/users' \
+--header 'Authorization: Bearer ABC123'
+--output output.json
+
+
+
+CSV Data Export
+
+
+curl
+--location 'https://starbasedb.YOUR-ID-HERE.workers.dev/export/csv/users' \
+--header 'Authorization: Bearer ABC123'
+--output output.csv
+
+
+
Contributing
We welcome contributions! Please refer to our Contribution Guide for more details.
diff --git a/src/export/csv.ts b/src/export/csv.ts
new file mode 100644
index 0000000..c708f2e
--- /dev/null
+++ b/src/export/csv.ts
@@ -0,0 +1,40 @@
+import { getTableData, createExportResponse } from './index';
+import { createResponse } from '../utils';
+
+export async function exportTableToCsvRoute(
+ sql: any,
+ operationQueue: any,
+ ctx: any,
+ processingOperation: { value: boolean },
+ tableName: string
+): Promise {
+ try {
+ const data = await getTableData(sql, operationQueue, ctx, processingOperation, tableName);
+
+ if (data === null) {
+ return createResponse(undefined, `Table '${tableName}' does not exist.`, 404);
+ }
+
+ // Convert the result to CSV
+ let csvContent = '';
+ if (data.length > 0) {
+ // Add headers
+ csvContent += Object.keys(data[0]).join(',') + '\n';
+
+ // Add data rows
+ data.forEach((row: any) => {
+ csvContent += Object.values(row).map(value => {
+ if (typeof value === 'string' && value.includes(',')) {
+ return `"${value.replace(/"/g, '""')}"`;
+ }
+ return value;
+ }).join(',') + '\n';
+ });
+ }
+
+ return createExportResponse(csvContent, `${tableName}_export.csv`, 'text/csv');
+ } catch (error: any) {
+ console.error('CSV Export Error:', error);
+ return createResponse(undefined, 'Failed to export table to CSV', 500);
+ }
+}
diff --git a/src/export/index.ts b/src/export/index.ts
new file mode 100644
index 0000000..60066b3
--- /dev/null
+++ b/src/export/index.ts
@@ -0,0 +1,50 @@
+import { enqueueOperation, processNextOperation } from '../operation';
+
+export async function getTableData(
+ sql: any,
+ operationQueue: any,
+ ctx: any,
+ processingOperation: { value: boolean },
+ tableName: string
+): Promise {
+ try {
+ // Verify if the table exists
+ const tableExistsResult = await enqueueOperation(
+ [{ sql: `SELECT name FROM sqlite_master WHERE type='table' AND name=?;`, params: [tableName] }],
+ false,
+ false,
+ operationQueue,
+ () => processNextOperation(sql, operationQueue, ctx, processingOperation)
+ );
+
+ if (tableExistsResult.result.length === 0) {
+ return null;
+ }
+
+ // Get table data
+ const dataResult = await enqueueOperation(
+ [{ sql: `SELECT * FROM ${tableName};` }],
+ false,
+ false,
+ operationQueue,
+ () => processNextOperation(sql, operationQueue, ctx, processingOperation)
+ );
+
+ return dataResult.result;
+ } catch (error: any) {
+ console.error('Table Data Fetch Error:', error);
+ throw error;
+ }
+}
+
+export function createExportResponse(data: any, fileName: string, contentType: string): Response {
+ const blob = new Blob([data], { type: contentType });
+
+ const headers = new Headers({
+ 'Content-Type': contentType,
+ 'Content-Disposition': `attachment; filename="${fileName}"`,
+ });
+
+ return new Response(blob, { headers });
+}
+
diff --git a/src/export/json.ts b/src/export/json.ts
new file mode 100644
index 0000000..d2ac413
--- /dev/null
+++ b/src/export/json.ts
@@ -0,0 +1,26 @@
+import { getTableData, createExportResponse } from './index';
+import { createResponse } from '../utils';
+
+export async function exportTableToJsonRoute(
+ sql: any,
+ operationQueue: any,
+ ctx: any,
+ processingOperation: { value: boolean },
+ tableName: string
+): Promise {
+ try {
+ const data = await getTableData(sql, operationQueue, ctx, processingOperation, tableName);
+
+ if (data === null) {
+ return createResponse(undefined, `Table '${tableName}' does not exist.`, 404);
+ }
+
+ // Convert the result to JSON
+ const jsonData = JSON.stringify(data, null, 4);
+
+ return createExportResponse(jsonData, `${tableName}_export.json`, 'application/json');
+ } catch (error: any) {
+ console.error('JSON Export Error:', error);
+ return createResponse(undefined, 'Failed to export table to JSON', 500);
+ }
+}
diff --git a/src/index.ts b/src/index.ts
index a614c4f..5dbc78c 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -4,6 +4,8 @@ import { enqueueOperation, OperationQueueItem, processNextOperation } from './op
import { LiteREST } from './literest';
import handleStudioRequest from "./studio";
import { dumpDatabaseRoute } from './export/dump';
+import { exportTableToJsonRoute } from './export/json';
+import { exportTableToCsvRoute } from './export/csv';
const DURABLE_OBJECT_ID = 'sql-durable-object';
@@ -125,8 +127,20 @@ export class DatabaseDurableObject extends DurableObject {
return this.statusRoute(request);
} else if (url.pathname.startsWith('/rest')) {
return await this.liteREST.handleRequest(request);
- } else if (request.method === 'GET' && url.pathname === '/dump') {
+ } else if (request.method === 'GET' && url.pathname === '/export/dump') {
return dumpDatabaseRoute(this.sql, this.operationQueue, this.ctx, this.processingOperation);
+ } else if (request.method === 'GET' && url.pathname.startsWith('/export/json/')) {
+ const tableName = url.pathname.split('/').pop();
+ if (!tableName) {
+ return createResponse(undefined, 'Table name is required', 400);
+ }
+ return exportTableToJsonRoute(this.sql, this.operationQueue, this.ctx, this.processingOperation, tableName);
+ } else if (request.method === 'GET' && url.pathname.startsWith('/export/csv/')) {
+ const tableName = url.pathname.split('/').pop();
+ if (!tableName) {
+ return createResponse(undefined, 'Table name is required', 400);
+ }
+ return exportTableToCsvRoute(this.sql, this.operationQueue, this.ctx, this.processingOperation, tableName);
} else {
return createResponse(undefined, 'Unknown operation', 400);
}
@@ -232,4 +246,4 @@ export default {
*/
return await stub.fetch(request);
},
-} satisfies ExportedHandler;
\ No newline at end of file
+} satisfies ExportedHandler;