From 7c339daabc2eb461fe3afe0db167735916ae1a17 Mon Sep 17 00:00:00 2001 From: Harshil Agrawal Date: Wed, 24 Jul 2024 18:17:36 +0200 Subject: [PATCH 1/9] [D1] D1 API tutorial --- .../build-an-api-to-access-d1/index.md | 336 ++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 content/d1/tutorials/build-an-api-to-access-d1/index.md diff --git a/content/d1/tutorials/build-an-api-to-access-d1/index.md b/content/d1/tutorials/build-an-api-to-access-d1/index.md new file mode 100644 index 00000000000000..288d92fcfbded7 --- /dev/null +++ b/content/d1/tutorials/build-an-api-to-access-d1/index.md @@ -0,0 +1,336 @@ +--- +updated: 2024-07-24 +difficulty: Intermediate +content_type: 📝 Tutorial +pcx_content_type: tutorial +title: Build an API to access D1 +products: [Workers] +tags: [Hono] +--- + +# Build an API to access D1 outside of your Workers or Pages project + +{{}} + +In this tutorial, you will learn how to create an API that allows you to securely run queries against a D1 database. This is useful if you want to access a D1 database outside of a Worker or Pages project. While you can query a D1 database via the [REST API](https://developers.cloudflare.com/api/operations/cloudflare-d1-create-database), this approach is not recommende due to rate limits and high latency. + +To access a D1 database outside of a Worker project, you need to create an API using a Worker. Your application can then securely interact with this API to run D1 queries. + +{{}} + +## Prerequisites + +- Ensure that you have [`Node.js`](https://nodejs.org/en/) installed on your machine. If not, use a Node version manager like [Volta](https://volta.sh/) or [nvm](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node.js versions. + +Note: [Wrangler](/workers/wrangler/install-and-update/) requires a Node version of `16.17.0` or later. + +- Sign up for a [Cloudflare account](https://dash.cloudflare.com/sign-up/workers-and-pages) if you have not already. + +## 1. Create a new project + +Use [C3](https://developers.cloudflare.com/learning-paths/workers/get-started/c3-and-wrangler/#c3), the command-line tool for Cloudflare's developer products, to create a new directory and initialize a new Worker project: + +```sh +$ npm create cloudflare d1-http +``` + +In your terminal, you will be asked a series of questions related to your project. Choose the following options: + +- For the `What type of application do you want to create?` prompt, select `"Hello World" Worker`. +- For the `Do you want to use TypeScript?` prompt, select `Yes`. +- For the `Do you want to use git for version control?` prompt, select `Yes`. +- For the `Do you want to deploy your application?` prompt, select `No`. + +To start developing, change into your new project directory: + +```sh +$ cd d1-http +``` + +## 2. Install Hono + +In this tutorial, you will use [Hono](https://github.com/honojs/hono), an Express.js-style framework, to build the API. To use Hono in this project, install it using `npm`: + +```sh +$ npm install hono +``` + +## 3. Initialise the application + +In the `src/index.ts` file, import the required packages, initialize a new Hono application, and configure the following middleware: + +- [Bearer Auth](https://hono.dev/docs/middleware/builtin/bearer-auth): Adds authentication to the API. +- [Logger](https://hono.dev/docs/middleware/builtin/logger): Allows monitoring the flow of requests and responses. +- [Pretty JSON](https://hono.dev/docs/middleware/builtin/pretty-json): Enables "JSON pretty print" for JSON response bodies. + +```ts +--- +filename: src/index.ts +--- +import { Hono } from 'hono'; +import { bearerAuth } from 'hono/bearer-auth'; +import { logger } from 'hono/logger'; +import { prettyJSON } from 'hono/pretty-json'; + +type Bindings = { + DB: D1Database; +}; + +const app = new Hono<{ Bindings: Bindings }>(); + +// update the token value +const token = 'YOUR_SECRET_TOKEN' + +app.use('*', bearerAuth({token}), prettyJSON(), logger()); +``` + +In the above code, replace `YOUR_SECRET_TOKEN` with a secret token you want to use for API authentication. + +## 4. Add API endpoints + +Next, in the `src/index.ts` file, add the following endpoints: + +- POST `/api/all` +- POST `/api/exec` +- POST `/api/batch` + +```ts +--- +filename: src/index.ts +--- + +... + +app.post('/api/all', async (c)=> { + return c.text('/api/all endpoint') +}) + +app.post('/api/exec', async (c)=> { + return c.text('/api/exec endpoint') +}) + +app.post('/api/batch', async (c)=> { + return c.text('/api/batch endpoint') +}) + +export default app; +``` + +Start the development server by running the following command: + +```sh +$ npm run dev +``` + +To test the API locally, execute the below cURL command: + +```sh +$ curl -H "Authorization: Bearer YOUR_SECRET_TOKEN" "http://localhost:8787/api/all" --data '{}' +``` + +You should get the following output + +```sh +/api/all endpoint +``` + +The Hono application is now set up. You can test the other enpoints and add more endpoints if needed. The API does not yet return any information from your database. In the next steps, you will create a database, add its bindings, and update the endpoints to interact with the database. + +## 5. Create a database + +If you don't have a D1 database already, you can create a new database with `wrangler d1 create`: + +```sh +$ npx wrangler d1 create d1-http-example +``` + +You might be asked to login to your Cloudflare. Once logged in, the command will create a new D1 database. You should see a similar output in your terminal. + +```sh +✅ Successfully created DB 'd1-http-example' in region EEUR +Created your new D1 database. + +[[d1_databases]] +binding = "DB" # i.e. available in your Worker on env.DB +database_name = "d1-http-example" +database_id = "1234567890" +``` + +Make a note of the displayed `database_name` and `database_id`. You will use this to reference to the database by creating a [binding](/workers/runtime-apis/bindings/). + +Open the `wrangler.toml` file, Wrangler's configuration file. Add the following binding in the file. Make sure that the `database_name` and the `database_id` are correct. + +```yml +[[d1_databases]] +binding = "DB" # i.e. available in your Worker on env.DB +database_name = "d1-http-example" +database_id = "1234567890" +``` + +You can now access the database in the Hono application. + +## 6. Create a table + +To create a table in your newly created database, create a new `schemas/schema.sql` file and paste the following SQL statement into the file. The code will drop any table named `posts` if it exists and then create a new table `posts` with the field `id`, `author`, `title`, `body`, and `post_slug`. + +```sql +--- +filename: schemas/schema.sql +--- +DROP TABLE IF EXISTS posts; +CREATE TABLE IF NOT EXISTS posts ( + id integer PRIMARY KEY AUTOINCREMENT, + author text NOT NULL, + title text NOT NULL, + body text NOT NULL, + post_slug text NOT NULL +); + +-- Uncomment the below statement to add data + +-- INSERT INTO posts (author, title, body, post_slug) VALUES ('Harshil', 'D1 HTTP API', 'Learn to create an API to query your D1 database.','d1-http-api'); +``` + +In your terminal, execute the following command to create this table: + +```sh +$ npx wrangler d1 execute d1-http-example --file=./schemas/schema.sql +``` + +Upon successful execution, a new table will be added to your database. + +{{}} + +## 7. Query the database + +Your application can now access the D1 database. In this step, you will update the API endpoints to query the database and return the result. + +In your `src/index.ts` file, update the code as follow. + +```ts +--- +filename: src/index.ts +--- +... +app.post('/api/all', async (c) => { + try { + let { query, params } = await c.req.json(); + let stmt = c.env.DB.prepare(query); + if (params) { + stmt = stmt.bind(params); + } + + const result = await stmt.all(); + return c.json(result); + } catch (err) { + return c.json({ error: `Failed to run query: ${err}` }, 500); + } +}); + +app.post('/api/exec', async (c) => { + try { + let { query } = await c.req.json(); + let result = await c.env.DB.exec(query); + return c.json(result); + } catch (err) { + return c.json({ error: `Failed to run query: ${err}` }, 500); + } +}); + +app.post('/api/batch', async (c) => { + try { + let { batch } = await c.req.json(); + let stmts = []; + for (let query of batch) { + let stmt = c.env.DB.prepare(query.query); + if (query.params) { + stmts.push(stmt.bind(query.params)); + } else { + stmts.push(stmt); + } + } + const results = await c.env.DB.batch(stmts); + return c.json(results); + } catch (err) { + return c.json({ error: `Failed to run query: ${err}` }, 500); + } +}); +... +``` + +In the above code, the endpoints are updated to receive `query` and `params`. These queries and parameters are passed to the respective functions to interact with the database. If the query is successful, you recieve the result from the database. If there is an error, the error message is returned. + +## 8. Test the API + +Now that the API can query the database, you can test it locally. If the local database is empty, populate it with some data. + +Start the development server by executing the following command: + +```sh +$ npm run dev +``` + +In a new terminal window, execute the following cURL commands. Make sure to replace `YOUR_SECRET_TOKEN` with the correct token. + +`/api/all` + +```sh +$ curl -H "Authorization: Bearer YOUR_SECRET_TOKEN" "http://localhost:8787/api/batch" --data '{"query": "SELECT title FROM posts WHERE id=?", "params":1}' +``` + +`/api/batch` + +```sh +$ curl -H "Authorization: Bearer YOUR_SECRET_TOKEN" "http://localhost:8787/api/batch" --data '{"batch": [ {"query": "SELECT title FROM posts WHERE id=?", "params":1},{"query": "SELECT id FROM posts"}]}' +``` + +`/api/exec` + +```sh +$ curl -H "Authorization: Bearer YOUR_SECRET_TOKEN" "http://localhost:8787/api/batch" --data '{"query": "INSERT INTO posts (author, title, body, post_slug) VALUES ('Harshil', 'D1 HTTP API', 'Learn to create an API to query your D1 database.','d1-http-api')" }' +``` + +If everything is implemented correctly, the above commands should result successful outputs. + +## 9. Deploy the API + +Now that everything is working as expected, the last step is to deploy it to the Cloudflare network. You will use Wrangler to deploy the API. The deployment involves two steps: + +1. Create a table in the production database +2. Deploy the app + +### 9.1 Create the table in the production database + +Until now, you were running a local instance of D1 database. To use the API in production, you need to add the table to your remote (production) database. To add the table to your production database, run the following command: + +```sh +$ npx wrangler d1 execute d1-http-example --file=./schemas/schema.sql --remote +``` + +You should now be able to view the table on the Cloudflare D1 dashboard. + +### 9.2 Deploy the app + +To deploy the application to the Cloudflare network, run the following command: + +```sh +$ npx wrangler deploy +``` + +Upon successful deployment, you will get the link of the deployed app in the terminal. Test it by running the following cURL command with the correct `YOUR_SECRET_TOKEN` and `WORKERS_URL`. + +```sh +$ curl -H "Authorization: Bearer YOUR_SECRET_TOKEN" "https://WORKERS_URL.workers.dev/api/exec" --data '{"query": "SELECT 1"}' +``` + +## Conclusion + +In this tutorial, you created an API that interacts with your D1 database. You deployed this API to the Workers. You can use this API in your external application to execute queries against your D1 database. To make your API more secure, sanitize the query before executing it. You can check out a similar implimentation that use Zod for validation in [this GitHub repository](https://github.com/elithrar/http-api-d1-example). From cc4c9a690ba301efd0af1222dc980642b91dca52 Mon Sep 17 00:00:00 2001 From: kodster28 Date: Wed, 24 Jul 2024 11:40:41 -0500 Subject: [PATCH 2/9] A few tweaks --- .../build-an-api-to-access-d1/index.md | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/content/d1/tutorials/build-an-api-to-access-d1/index.md b/content/d1/tutorials/build-an-api-to-access-d1/index.md index 288d92fcfbded7..ce90d789934d3f 100644 --- a/content/d1/tutorials/build-an-api-to-access-d1/index.md +++ b/content/d1/tutorials/build-an-api-to-access-d1/index.md @@ -12,7 +12,7 @@ tags: [Hono] {{}} -In this tutorial, you will learn how to create an API that allows you to securely run queries against a D1 database. This is useful if you want to access a D1 database outside of a Worker or Pages project. While you can query a D1 database via the [REST API](https://developers.cloudflare.com/api/operations/cloudflare-d1-create-database), this approach is not recommende due to rate limits and high latency. +In this tutorial, you will learn how to create an API that allows you to securely run queries against a D1 database. This is useful if you want to access a D1 database outside of a Worker or Pages project. While you can query a D1 database via the [REST API](/api/operations/cloudflare-d1-create-database), this approach is not recommende due to rate limits and high latency. To access a D1 database outside of a Worker project, you need to create an API using a Worker. Your application can then securely interact with this API to run D1 queries. @@ -24,15 +24,11 @@ The tutorial does not cover how to sanitize SQL queries. Please ensure that you ## Prerequisites -- Ensure that you have [`Node.js`](https://nodejs.org/en/) installed on your machine. If not, use a Node version manager like [Volta](https://volta.sh/) or [nvm](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node.js versions. - -Note: [Wrangler](/workers/wrangler/install-and-update/) requires a Node version of `16.17.0` or later. - -- Sign up for a [Cloudflare account](https://dash.cloudflare.com/sign-up/workers-and-pages) if you have not already. +{{}} ## 1. Create a new project -Use [C3](https://developers.cloudflare.com/learning-paths/workers/get-started/c3-and-wrangler/#c3), the command-line tool for Cloudflare's developer products, to create a new directory and initialize a new Worker project: +Use [C3](/learning-paths/workers/get-started/c3-and-wrangler/#c3), the command-line tool for Cloudflare's developer products, to create a new directory and initialize a new Worker project: ```sh $ npm create cloudflare d1-http @@ -134,7 +130,7 @@ $ curl -H "Authorization: Bearer YOUR_SECRET_TOKEN" "http://localhost:8787/api/a You should get the following output -```sh +```txt /api/all endpoint ``` @@ -150,7 +146,7 @@ $ npx wrangler d1 create d1-http-example You might be asked to login to your Cloudflare. Once logged in, the command will create a new D1 database. You should see a similar output in your terminal. -```sh +```txt ✅ Successfully created DB 'd1-http-example' in region EEUR Created your new D1 database. @@ -164,7 +160,7 @@ Make a note of the displayed `database_name` and `database_id`. You will use thi Open the `wrangler.toml` file, Wrangler's configuration file. Add the following binding in the file. Make sure that the `database_name` and the `database_id` are correct. -```yml +```toml [[d1_databases]] binding = "DB" # i.e. available in your Worker on env.DB database_name = "d1-http-example" @@ -280,21 +276,24 @@ $ npm run dev In a new terminal window, execute the following cURL commands. Make sure to replace `YOUR_SECRET_TOKEN` with the correct token. -`/api/all` - ```sh +--- +header: /api/all +--- $ curl -H "Authorization: Bearer YOUR_SECRET_TOKEN" "http://localhost:8787/api/batch" --data '{"query": "SELECT title FROM posts WHERE id=?", "params":1}' ``` -`/api/batch` - ```sh +--- +header: /api/batch +--- $ curl -H "Authorization: Bearer YOUR_SECRET_TOKEN" "http://localhost:8787/api/batch" --data '{"batch": [ {"query": "SELECT title FROM posts WHERE id=?", "params":1},{"query": "SELECT id FROM posts"}]}' ``` -`/api/exec` - ```sh +--- +header: /api/exec +--- $ curl -H "Authorization: Bearer YOUR_SECRET_TOKEN" "http://localhost:8787/api/batch" --data '{"query": "INSERT INTO posts (author, title, body, post_slug) VALUES ('Harshil', 'D1 HTTP API', 'Learn to create an API to query your D1 database.','d1-http-api')" }' ``` From 32a5f90b4cc3aa5268395d938874d093fd743d3a Mon Sep 17 00:00:00 2001 From: Harshil Agrawal Date: Fri, 26 Jul 2024 11:07:28 +0200 Subject: [PATCH 3/9] update API endpoints and schema.sql file code --- .../d1/tutorials/build-an-api-to-access-d1/index.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/content/d1/tutorials/build-an-api-to-access-d1/index.md b/content/d1/tutorials/build-an-api-to-access-d1/index.md index ce90d789934d3f..7eabffa64cecca 100644 --- a/content/d1/tutorials/build-an-api-to-access-d1/index.md +++ b/content/d1/tutorials/build-an-api-to-access-d1/index.md @@ -185,10 +185,12 @@ CREATE TABLE IF NOT EXISTS posts ( body text NOT NULL, post_slug text NOT NULL ); +``` --- Uncomment the below statement to add data +Optinally, you can add the below INSERT statement to populate the table. --- INSERT INTO posts (author, title, body, post_slug) VALUES ('Harshil', 'D1 HTTP API', 'Learn to create an API to query your D1 database.','d1-http-api'); +```sql +INSERT INTO posts (author, title, body, post_slug) VALUES ('Harshil', 'D1 HTTP API', 'Learn to create an API to query your D1 database.','d1-http-api'); ``` In your terminal, execute the following command to create this table: @@ -280,7 +282,7 @@ In a new terminal window, execute the following cURL commands. Make sure to repl --- header: /api/all --- -$ curl -H "Authorization: Bearer YOUR_SECRET_TOKEN" "http://localhost:8787/api/batch" --data '{"query": "SELECT title FROM posts WHERE id=?", "params":1}' +$ curl -H "Authorization: Bearer YOUR_SECRET_TOKEN" "http://localhost:8787/api/all" --data '{"query": "SELECT title FROM posts WHERE id=?", "params":1}' ``` ```sh @@ -294,7 +296,7 @@ $ curl -H "Authorization: Bearer YOUR_SECRET_TOKEN" "http://localhost:8787/api/b --- header: /api/exec --- -$ curl -H "Authorization: Bearer YOUR_SECRET_TOKEN" "http://localhost:8787/api/batch" --data '{"query": "INSERT INTO posts (author, title, body, post_slug) VALUES ('Harshil', 'D1 HTTP API', 'Learn to create an API to query your D1 database.','d1-http-api')" }' +$ curl -H "Authorization: Bearer YOUR_SECRET_TOKEN" "localhost:8787/api/exec" --data '{"query": "INSERT INTO posts (author, title, body, post_slug) VALUES ('\''Harshil'\'', '\''D1 HTTP API'\'', '\''Learn to create an API to query your D1 database.'\'','\''d1-http-api'\'')" }' ``` If everything is implemented correctly, the above commands should result successful outputs. From 7cb60b4815fad4ce9e504e261ec165ddc7e8d66a Mon Sep 17 00:00:00 2001 From: Harshil Agrawal Date: Fri, 26 Jul 2024 12:53:57 +0200 Subject: [PATCH 4/9] update API_KEY management steps --- .../build-an-api-to-access-d1/index.md | 67 +++++++++++++------ 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/content/d1/tutorials/build-an-api-to-access-d1/index.md b/content/d1/tutorials/build-an-api-to-access-d1/index.md index 7eabffa64cecca..444232af13c8aa 100644 --- a/content/d1/tutorials/build-an-api-to-access-d1/index.md +++ b/content/d1/tutorials/build-an-api-to-access-d1/index.md @@ -55,7 +55,23 @@ In this tutorial, you will use [Hono](https://github.com/honojs/hono), an Expres $ npm install hono ``` -## 3. Initialise the application +## 3. Add API_KEY + +To ensure that the API Key is secure, you will add it as a [secret](https://developers.cloudflare.com/workers/configuration/secrets/). To do so, create a `.dev.vars` file in the root directory. Add your API key in the file as follows. + +```env +API_KEY="YOUR_API_KEY" +``` + +Replace `YOUR_API_KEY` with a valid string value. You can also generate this value using the following command. + +```sh +$ openssl rand -base64 32 +``` + +Your Hono applicaton will use this API key for authentication. + +## 4. Initialise the application In the `src/index.ts` file, import the required packages, initialize a new Hono application, and configure the following middleware: @@ -74,19 +90,18 @@ import { prettyJSON } from 'hono/pretty-json'; type Bindings = { DB: D1Database; + API_KEY: string; }; const app = new Hono<{ Bindings: Bindings }>(); -// update the token value -const token = 'YOUR_SECRET_TOKEN' - -app.use('*', bearerAuth({token}), prettyJSON(), logger()); +app.use('*', prettyJSON(), logger(), async (c, next) => { + const auth = bearerAuth({ token: c.env.API_KEY }); + return auth(c, next); +});; ``` -In the above code, replace `YOUR_SECRET_TOKEN` with a secret token you want to use for API authentication. - -## 4. Add API endpoints +## 5. Add API endpoints Next, in the `src/index.ts` file, add the following endpoints: @@ -122,10 +137,10 @@ Start the development server by running the following command: $ npm run dev ``` -To test the API locally, execute the below cURL command: +To test the API locally, execute the below cURL command. Replace `YOUR_API_KEY` with the value you set in the `.dev.vars` file. ```sh -$ curl -H "Authorization: Bearer YOUR_SECRET_TOKEN" "http://localhost:8787/api/all" --data '{}' +$ curl -H "Authorization: Bearer YOUR_API_KEY" "http://localhost:8787/api/all" --data '{}' ``` You should get the following output @@ -136,7 +151,7 @@ You should get the following output The Hono application is now set up. You can test the other enpoints and add more endpoints if needed. The API does not yet return any information from your database. In the next steps, you will create a database, add its bindings, and update the endpoints to interact with the database. -## 5. Create a database +## 6. Create a database If you don't have a D1 database already, you can create a new database with `wrangler d1 create`: @@ -169,7 +184,7 @@ database_id = "1234567890" You can now access the database in the Hono application. -## 6. Create a table +## 7. Create a table To create a table in your newly created database, create a new `schemas/schema.sql` file and paste the following SQL statement into the file. The code will drop any table named `posts` if it exists and then create a new table `posts` with the field `id`, `author`, `title`, `body`, and `post_slug`. @@ -207,7 +222,7 @@ The table will be created in the local instance of the database. If you want to {{}} -## 7. Query the database +## 8. Query the database Your application can now access the D1 database. In this step, you will update the API endpoints to query the database and return the result. @@ -266,7 +281,7 @@ app.post('/api/batch', async (c) => { In the above code, the endpoints are updated to receive `query` and `params`. These queries and parameters are passed to the respective functions to interact with the database. If the query is successful, you recieve the result from the database. If there is an error, the error message is returned. -## 8. Test the API +## 9. Test the API Now that the API can query the database, you can test it locally. If the local database is empty, populate it with some data. @@ -276,32 +291,32 @@ Start the development server by executing the following command: $ npm run dev ``` -In a new terminal window, execute the following cURL commands. Make sure to replace `YOUR_SECRET_TOKEN` with the correct token. +In a new terminal window, execute the following cURL commands. Make sure to replace `YOUR_API_KEY` with the correct value. ```sh --- header: /api/all --- -$ curl -H "Authorization: Bearer YOUR_SECRET_TOKEN" "http://localhost:8787/api/all" --data '{"query": "SELECT title FROM posts WHERE id=?", "params":1}' +$ curl -H "Authorization: Bearer YOUR_API_KEY" "http://localhost:8787/api/all" --data '{"query": "SELECT title FROM posts WHERE id=?", "params":1}' ``` ```sh --- header: /api/batch --- -$ curl -H "Authorization: Bearer YOUR_SECRET_TOKEN" "http://localhost:8787/api/batch" --data '{"batch": [ {"query": "SELECT title FROM posts WHERE id=?", "params":1},{"query": "SELECT id FROM posts"}]}' +$ curl -H "Authorization: Bearer YOUR_API_KEY" "http://localhost:8787/api/batch" --data '{"batch": [ {"query": "SELECT title FROM posts WHERE id=?", "params":1},{"query": "SELECT id FROM posts"}]}' ``` ```sh --- header: /api/exec --- -$ curl -H "Authorization: Bearer YOUR_SECRET_TOKEN" "localhost:8787/api/exec" --data '{"query": "INSERT INTO posts (author, title, body, post_slug) VALUES ('\''Harshil'\'', '\''D1 HTTP API'\'', '\''Learn to create an API to query your D1 database.'\'','\''d1-http-api'\'')" }' +$ curl -H "Authorization: Bearer YOUR_API_KEY" "localhost:8787/api/exec" --data '{"query": "INSERT INTO posts (author, title, body, post_slug) VALUES ('\''Harshil'\'', '\''D1 HTTP API'\'', '\''Learn to create an API to query your D1 database.'\'','\''d1-http-api'\'')" }' ``` If everything is implemented correctly, the above commands should result successful outputs. -## 9. Deploy the API +## 10. Deploy the API Now that everything is working as expected, the last step is to deploy it to the Cloudflare network. You will use Wrangler to deploy the API. The deployment involves two steps: @@ -326,10 +341,20 @@ To deploy the application to the Cloudflare network, run the following command: $ npx wrangler deploy ``` -Upon successful deployment, you will get the link of the deployed app in the terminal. Test it by running the following cURL command with the correct `YOUR_SECRET_TOKEN` and `WORKERS_URL`. +Upon successful deployment, you will get the link of the deployed app in the terminal. Make a note of it. + +Next, execute the `wrangler secret put ` command to add the `API_KEY` to the deployed project. + +```sh +$ npx wrangler secret put API_KEY +``` + +Enter the value of your API key. Your API key will get added to your project. Using this value you can make secure API calls to your deployed API. + +To test it, run the following cURL command with the correct `YOUR_API_KEY` and `WORKERS_URL`. ```sh -$ curl -H "Authorization: Bearer YOUR_SECRET_TOKEN" "https://WORKERS_URL.workers.dev/api/exec" --data '{"query": "SELECT 1"}' +$ curl -H "Authorization: Bearer YOUR_API_KEY" "https://WORKERS_URL.workers.dev/api/exec" --data '{"query": "SELECT 1"}' ``` ## Conclusion From 733d38b6336c8e7f352e706046980f277f7acdd4 Mon Sep 17 00:00:00 2001 From: Harshil Agrawal Date: Fri, 26 Jul 2024 12:54:30 +0200 Subject: [PATCH 5/9] update the updated date --- content/d1/tutorials/build-an-api-to-access-d1/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/d1/tutorials/build-an-api-to-access-d1/index.md b/content/d1/tutorials/build-an-api-to-access-d1/index.md index 444232af13c8aa..f454e221d662da 100644 --- a/content/d1/tutorials/build-an-api-to-access-d1/index.md +++ b/content/d1/tutorials/build-an-api-to-access-d1/index.md @@ -1,5 +1,5 @@ --- -updated: 2024-07-24 +updated: 2024-07-26 difficulty: Intermediate content_type: 📝 Tutorial pcx_content_type: tutorial From e83b87acbc4d94712b3c1010b14c1292ed384ca1 Mon Sep 17 00:00:00 2001 From: Harshil Agrawal Date: Fri, 26 Jul 2024 12:56:49 +0200 Subject: [PATCH 6/9] update relative link --- content/d1/tutorials/build-an-api-to-access-d1/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/d1/tutorials/build-an-api-to-access-d1/index.md b/content/d1/tutorials/build-an-api-to-access-d1/index.md index f454e221d662da..54dab1b0ce3c06 100644 --- a/content/d1/tutorials/build-an-api-to-access-d1/index.md +++ b/content/d1/tutorials/build-an-api-to-access-d1/index.md @@ -57,7 +57,7 @@ $ npm install hono ## 3. Add API_KEY -To ensure that the API Key is secure, you will add it as a [secret](https://developers.cloudflare.com/workers/configuration/secrets/). To do so, create a `.dev.vars` file in the root directory. Add your API key in the file as follows. +To ensure that the API Key is secure, you will add it as a [secret](/workers/configuration/secrets/). To do so, create a `.dev.vars` file in the root directory. Add your API key in the file as follows. ```env API_KEY="YOUR_API_KEY" From 94b8c5494dd4fa65ffbe163f56fb12109a074848 Mon Sep 17 00:00:00 2001 From: Harshil Agrawal <18901032+harshil1712@users.noreply.github.com> Date: Tue, 30 Jul 2024 18:46:12 +0200 Subject: [PATCH 7/9] Update content/d1/tutorials/build-an-api-to-access-d1/index.md Co-authored-by: Matt Silverlock --- content/d1/tutorials/build-an-api-to-access-d1/index.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/content/d1/tutorials/build-an-api-to-access-d1/index.md b/content/d1/tutorials/build-an-api-to-access-d1/index.md index 54dab1b0ce3c06..768f4501c9f485 100644 --- a/content/d1/tutorials/build-an-api-to-access-d1/index.md +++ b/content/d1/tutorials/build-an-api-to-access-d1/index.md @@ -12,7 +12,11 @@ tags: [Hono] {{}} -In this tutorial, you will learn how to create an API that allows you to securely run queries against a D1 database. This is useful if you want to access a D1 database outside of a Worker or Pages project. While you can query a D1 database via the [REST API](/api/operations/cloudflare-d1-create-database), this approach is not recommende due to rate limits and high latency. +In this tutorial, you will learn how to create an API that allows you to securely run queries against a D1 database. + +This is useful if you want to access a D1 database outside of a Worker or Pages project, customize access controls and/or limit what tables can be queried. + +D1's built-in [REST API](/api/operations/cloudflare-d1-create-database) is best suited for administrative use as the global [Cloudflare API rate limit](/fundamentals/api/reference/limits/) applies. To access a D1 database outside of a Worker project, you need to create an API using a Worker. Your application can then securely interact with this API to run D1 queries. From 0ea3f4c8b8b6e1b59ae490b99a2a495eaa1e3b77 Mon Sep 17 00:00:00 2001 From: Harshil Agrawal Date: Wed, 31 Jul 2024 11:50:31 +0200 Subject: [PATCH 8/9] update title and other fixes --- .../build-an-api-to-access-d1/index.md | 68 +++++++++++++------ 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/content/d1/tutorials/build-an-api-to-access-d1/index.md b/content/d1/tutorials/build-an-api-to-access-d1/index.md index 768f4501c9f485..1c35df54101d5c 100644 --- a/content/d1/tutorials/build-an-api-to-access-d1/index.md +++ b/content/d1/tutorials/build-an-api-to-access-d1/index.md @@ -1,14 +1,14 @@ --- -updated: 2024-07-26 +updated: 2024-07-31 difficulty: Intermediate content_type: 📝 Tutorial pcx_content_type: tutorial -title: Build an API to access D1 +title: Build an API to access D1 using a proxy Worker products: [Workers] tags: [Hono] --- -# Build an API to access D1 outside of your Workers or Pages project +# Build an API to access D1 using a proxy Worker {{}} @@ -22,7 +22,7 @@ To access a D1 database outside of a Worker project, you need to create an API u {{}} @@ -35,7 +35,7 @@ The tutorial does not cover how to sanitize SQL queries. Please ensure that you Use [C3](/learning-paths/workers/get-started/c3-and-wrangler/#c3), the command-line tool for Cloudflare's developer products, to create a new directory and initialize a new Worker project: ```sh -$ npm create cloudflare d1-http +$ npm create cloudflare@latest d1-http ``` In your terminal, you will be asked a series of questions related to your project. Choose the following options: @@ -61,7 +61,7 @@ $ npm install hono ## 3. Add API_KEY -To ensure that the API Key is secure, you will add it as a [secret](/workers/configuration/secrets/). To do so, create a `.dev.vars` file in the root directory. Add your API key in the file as follows. +You will need an API Key to make authenticated calls to the API. To ensure that the API Key is secure, you will add it as a [secret](/workers/configuration/secrets/). For local development, create a `.dev.vars` file in the root directory. Add your API key in the file as follows. ```env API_KEY="YOUR_API_KEY" @@ -73,8 +73,6 @@ Replace `YOUR_API_KEY` with a valid string value. You can also generate this val $ openssl rand -base64 32 ``` -Your Hono applicaton will use this API key for authentication. - ## 4. Initialise the application In the `src/index.ts` file, import the required packages, initialize a new Hono application, and configure the following middleware: @@ -93,7 +91,6 @@ import { logger } from 'hono/logger'; import { prettyJSON } from 'hono/pretty-json'; type Bindings = { - DB: D1Database; API_KEY: string; }; @@ -118,18 +115,18 @@ Next, in the `src/index.ts` file, add the following endpoints: filename: src/index.ts --- -... +// Paste this code at the end of the src/index.ts file app.post('/api/all', async (c)=> { - return c.text('/api/all endpoint') + return c.text('/api/all endpoint'); }) app.post('/api/exec', async (c)=> { - return c.text('/api/exec endpoint') + return c.text('/api/exec endpoint'); }) app.post('/api/batch', async (c)=> { - return c.text('/api/batch endpoint') + return c.text('/api/batch endpoint'); }) export default app; @@ -177,6 +174,8 @@ database_id = "1234567890" Make a note of the displayed `database_name` and `database_id`. You will use this to reference to the database by creating a [binding](/workers/runtime-apis/bindings/). +## 7. Add bindings + Open the `wrangler.toml` file, Wrangler's configuration file. Add the following binding in the file. Make sure that the `database_name` and the `database_id` are correct. ```toml @@ -186,9 +185,18 @@ database_name = "d1-http-example" database_id = "1234567890" ``` +Next, in your `src/index.ts` file, update the `Bindings` type and add `DB: D1Database`. The updated type should be as follow: + +```ts +type Bindings = { + DB: D1Database; + API_KEY: string; +}; +``` + You can now access the database in the Hono application. -## 7. Create a table +## 8. Create a table To create a table in your newly created database, create a new `schemas/schema.sql` file and paste the following SQL statement into the file. The code will drop any table named `posts` if it exists and then create a new table `posts` with the field `id`, `author`, `title`, `body`, and `post_slug`. @@ -226,7 +234,7 @@ The table will be created in the local instance of the database. If you want to {{}} -## 8. Query the database +## 9. Query the database Your application can now access the D1 database. In this step, you will update the API endpoints to query the database and return the result. @@ -236,7 +244,13 @@ In your `src/index.ts` file, update the code as follow. --- filename: src/index.ts --- -... +// Update the API routes + +/** + * Executes the `stmt.all()` method. + * https://developers.cloudflare.com/d1/build-with-d1/d1-client-api/#await-stmtall + */ + app.post('/api/all', async (c) => { try { let { query, params } = await c.req.json(); @@ -252,6 +266,11 @@ app.post('/api/all', async (c) => { } }); +/** + * Executes the `db.exec()` method. + * https://developers.cloudflare.com/d1/build-with-d1/d1-client-api/#await-dbexec + */ + app.post('/api/exec', async (c) => { try { let { query } = await c.req.json(); @@ -262,6 +281,11 @@ app.post('/api/exec', async (c) => { } }); +/** + * Executes the `db.batch()` method. + * https://developers.cloudflare.com/d1/build-with-d1/d1-client-api/#dbbatch + */ + app.post('/api/batch', async (c) => { try { let { batch } = await c.req.json(); @@ -285,7 +309,7 @@ app.post('/api/batch', async (c) => { In the above code, the endpoints are updated to receive `query` and `params`. These queries and parameters are passed to the respective functions to interact with the database. If the query is successful, you recieve the result from the database. If there is an error, the error message is returned. -## 9. Test the API +## 10. Test the API Now that the API can query the database, you can test it locally. If the local database is empty, populate it with some data. @@ -320,14 +344,14 @@ $ curl -H "Authorization: Bearer YOUR_API_KEY" "localhost:8787/api/exec" --data If everything is implemented correctly, the above commands should result successful outputs. -## 10. Deploy the API +## 11. Deploy the API Now that everything is working as expected, the last step is to deploy it to the Cloudflare network. You will use Wrangler to deploy the API. The deployment involves two steps: 1. Create a table in the production database 2. Deploy the app -### 9.1 Create the table in the production database +### 11.1 Create the table in the production database Until now, you were running a local instance of D1 database. To use the API in production, you need to add the table to your remote (production) database. To add the table to your production database, run the following command: @@ -337,7 +361,7 @@ $ npx wrangler d1 execute d1-http-example --file=./schemas/schema.sql --remote You should now be able to view the table on the Cloudflare D1 dashboard. -### 9.2 Deploy the app +### 11.2 Deploy the app To deploy the application to the Cloudflare network, run the following command: @@ -347,7 +371,7 @@ $ npx wrangler deploy Upon successful deployment, you will get the link of the deployed app in the terminal. Make a note of it. -Next, execute the `wrangler secret put ` command to add the `API_KEY` to the deployed project. +Next, generate a new API key to use in production and then execute the `wrangler secret put ` command to add the `API_KEY` to the deployed project. ```sh $ npx wrangler secret put API_KEY @@ -363,4 +387,4 @@ $ curl -H "Authorization: Bearer YOUR_API_KEY" "https://WORKERS_URL.workers.dev/ ## Conclusion -In this tutorial, you created an API that interacts with your D1 database. You deployed this API to the Workers. You can use this API in your external application to execute queries against your D1 database. To make your API more secure, sanitize the query before executing it. You can check out a similar implimentation that use Zod for validation in [this GitHub repository](https://github.com/elithrar/http-api-d1-example). +In this tutorial, you created an API that interacts with your D1 database. You deployed this API to the Workers. You can use this API in your external application to execute queries against your D1 database. You can check out a similar implimentation that use Zod for validation in [this GitHub repository](https://github.com/elithrar/http-api-d1-example). If you want to build an OpenAPI compliant API for your D1 database, you should use the [Cloudflare Workers OpenAPI 3.1 template](https://github.com/cloudflare/workers-sdk/tree/main/templates/worker-openapi). From 3a36026649f0bcd756f5aeb18a0a5850fd0dca63 Mon Sep 17 00:00:00 2001 From: Harshil Agrawal Date: Tue, 6 Aug 2024 12:20:24 +0200 Subject: [PATCH 9/9] add link to finished GitHub repo --- content/d1/tutorials/build-an-api-to-access-d1/index.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/content/d1/tutorials/build-an-api-to-access-d1/index.md b/content/d1/tutorials/build-an-api-to-access-d1/index.md index 1c35df54101d5c..afe566a27f6750 100644 --- a/content/d1/tutorials/build-an-api-to-access-d1/index.md +++ b/content/d1/tutorials/build-an-api-to-access-d1/index.md @@ -1,5 +1,5 @@ --- -updated: 2024-07-31 +updated: 2024-08-06 difficulty: Intermediate content_type: 📝 Tutorial pcx_content_type: tutorial @@ -387,4 +387,6 @@ $ curl -H "Authorization: Bearer YOUR_API_KEY" "https://WORKERS_URL.workers.dev/ ## Conclusion -In this tutorial, you created an API that interacts with your D1 database. You deployed this API to the Workers. You can use this API in your external application to execute queries against your D1 database. You can check out a similar implimentation that use Zod for validation in [this GitHub repository](https://github.com/elithrar/http-api-d1-example). If you want to build an OpenAPI compliant API for your D1 database, you should use the [Cloudflare Workers OpenAPI 3.1 template](https://github.com/cloudflare/workers-sdk/tree/main/templates/worker-openapi). +In this tutorial, you created an API that interacts with your D1 database. You deployed this API to the Workers. You can use this API in your external application to execute queries against your D1 database. The full code for this tutorial can be found on [GitHub](https://github.com/harshil1712/d1-http-example/tree/main). + +You can check out a similar implimentation that use Zod for validation in [this GitHub repository](https://github.com/elithrar/http-api-d1-example). If you want to build an OpenAPI compliant API for your D1 database, you should use the [Cloudflare Workers OpenAPI 3.1 template](https://github.com/cloudflare/workers-sdk/tree/main/templates/worker-openapi).