@planetscale/database
Advanced tools
Comparing version 0.5.0 to 0.6.0
@@ -28,2 +28,3 @@ export { format } from './sanitization.js'; | ||
} | ||
export declare type Cast = typeof cast; | ||
export interface Config { | ||
@@ -36,3 +37,16 @@ url?: string; | ||
format?: (query: string, args: any) => string; | ||
cast?: Cast; | ||
} | ||
export interface Field { | ||
name: string; | ||
type: string; | ||
table?: string; | ||
orgTable?: string | null; | ||
database?: string | null; | ||
orgName?: string | null; | ||
columnLength?: number | null; | ||
charset?: number | null; | ||
flags?: number | null; | ||
columnType?: string | null; | ||
} | ||
export declare class Client { | ||
@@ -49,6 +63,6 @@ private config; | ||
refresh(): Promise<void>; | ||
execute(query: string, args?: any): Promise<ExecutedQuery>; | ||
private createSession; | ||
private postJSON; | ||
execute(query: string, args?: any): Promise<ExecutedQuery>; | ||
} | ||
export declare function connect(config: Config): Connection; | ||
export declare function cast(field: Field, value: string | null): number | string | null; |
@@ -5,2 +5,3 @@ import { format } from './sanitization.js'; | ||
import { decode } from './text.js'; | ||
const PACKAGE_VERSION = '0.5.0'; | ||
export class DatabaseError extends Error { | ||
@@ -43,37 +44,2 @@ constructor(message, status, body) { | ||
} | ||
async createSession() { | ||
const url = new URL('/psdb.v1alpha1.Database/CreateSession', `https://${this.config.host}`); | ||
const { session } = await this.postJSON(url); | ||
this.session = session; | ||
return session; | ||
} | ||
async postJSON(url, body = {}) { | ||
const auth = btoa(`${this.config.username}:${this.config.password}`); | ||
const { fetch } = this.config; | ||
const response = await fetch(url.toString(), { | ||
method: 'POST', | ||
body: JSON.stringify(body), | ||
headers: { | ||
'Content-Type': 'application/json', | ||
Authorization: `Basic ${auth}` | ||
} | ||
}); | ||
if (response.ok) { | ||
return await response.json(); | ||
} | ||
else { | ||
let error = null; | ||
try { | ||
const e = (await response.json()).error; | ||
error = new DatabaseError(e.message, response.status, e); | ||
} | ||
catch { | ||
error = new DatabaseError(response.statusText, response.status, { | ||
code: 'internal', | ||
message: response.statusText | ||
}); | ||
} | ||
throw error; | ||
} | ||
} | ||
async execute(query, args) { | ||
@@ -84,3 +50,3 @@ const url = new URL('/psdb.v1alpha1.Database/Execute', `https://${this.config.host}`); | ||
const start = Date.now(); | ||
const saved = await this.postJSON(url, { query: sql, session: this.session }); | ||
const saved = await postJSON(this.config, url, { query: sql, session: this.session }); | ||
const time = Date.now() - start; | ||
@@ -91,3 +57,3 @@ const { result, session, error } = saved; | ||
this.session = session; | ||
const rows = result ? parse(result) : []; | ||
const rows = result ? parse(result, this.config.cast || cast) : []; | ||
const headers = result ? result.fields?.map((f) => f.name) ?? [] : []; | ||
@@ -108,17 +74,53 @@ const typeByName = (acc, { name, type }) => ({ ...acc, [name]: type }); | ||
} | ||
async createSession() { | ||
const url = new URL('/psdb.v1alpha1.Database/CreateSession', `https://${this.config.host}`); | ||
const { session } = await postJSON(this.config, url); | ||
this.session = session; | ||
return session; | ||
} | ||
} | ||
async function postJSON(config, url, body = {}) { | ||
const auth = btoa(`${config.username}:${config.password}`); | ||
const { fetch } = config; | ||
const response = await fetch(url.toString(), { | ||
method: 'POST', | ||
body: JSON.stringify(body), | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'User-Agent': `database-js/${PACKAGE_VERSION}`, | ||
Authorization: `Basic ${auth}` | ||
} | ||
}); | ||
if (response.ok) { | ||
return await response.json(); | ||
} | ||
else { | ||
let error = null; | ||
try { | ||
const e = (await response.json()).error; | ||
error = new DatabaseError(e.message, response.status, e); | ||
} | ||
catch { | ||
error = new DatabaseError(response.statusText, response.status, { | ||
code: 'internal', | ||
message: response.statusText | ||
}); | ||
} | ||
throw error; | ||
} | ||
} | ||
export function connect(config) { | ||
return new Connection(config); | ||
} | ||
function parseRow(fields, rawRow) { | ||
function parseRow(fields, rawRow, cast) { | ||
const row = decodeRow(rawRow); | ||
return fields.reduce((acc, field, ix) => { | ||
acc[field.name] = parseColumn(field.type, row[ix]); | ||
acc[field.name] = cast(field, row[ix]); | ||
return acc; | ||
}, {}); | ||
} | ||
function parse(result) { | ||
function parse(result, cast) { | ||
const fields = result.fields; | ||
const rows = result.rows ?? []; | ||
return rows.map((row) => parseRow(fields, row)); | ||
return rows.map((row) => parseRow(fields, row, cast)); | ||
} | ||
@@ -137,7 +139,7 @@ function decodeRow(row) { | ||
} | ||
function parseColumn(type, value) { | ||
export function cast(field, value) { | ||
if (value === '' || value == null) { | ||
return value; | ||
} | ||
switch (type) { | ||
switch (field.type) { | ||
case 'INT8': | ||
@@ -147,3 +149,2 @@ case 'INT16': | ||
case 'INT32': | ||
case 'INT64': | ||
case 'UINT8': | ||
@@ -153,3 +154,2 @@ case 'UINT16': | ||
case 'UINT32': | ||
case 'UINT64': | ||
case 'YEAR': | ||
@@ -159,4 +159,6 @@ return parseInt(value, 10); | ||
case 'FLOAT64': | ||
return parseFloat(value); | ||
case 'DECIMAL': | ||
return parseFloat(value); | ||
case 'INT64': | ||
case 'UINT64': | ||
case 'DATE': | ||
@@ -163,0 +165,0 @@ case 'TIME': |
{ | ||
"name": "@planetscale/database", | ||
"version": "0.5.0", | ||
"version": "0.6.0", | ||
"description": "A JavaScript client for PlanetScale databases.", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -1,4 +0,4 @@ | ||
# PlanetScale database client | ||
# PlanetScale Serverless Driver for JavaScript | ||
A JavaScript client for PlanetScale databases. | ||
A Fetch API-compatible PlanetScale database driver for serverless and edge compute platforms that require HTTP external connections, such as Cloudflare Workers or Vercel Edge Functions | ||
@@ -8,3 +8,3 @@ ## Installation | ||
``` | ||
$ npm install @planetscale/database | ||
npm install @planetscale/database | ||
``` | ||
@@ -36,3 +36,3 @@ | ||
const config = { | ||
url: process.env['DATABASE_URL'] || 'mysql://user:pass@aws.connect.psdb.cloud' | ||
url: process.env['DATABASE_URL'] || 'mysql://user:pass@host' | ||
} | ||
@@ -51,3 +51,3 @@ | ||
const client = new Client({ | ||
host: 'aws.connect.psdb.cloud', | ||
host: '<host>', | ||
username: '<user>', | ||
@@ -74,3 +74,3 @@ password: '<password>' | ||
fetch, | ||
host: 'aws.connect.psdb.cloud', | ||
host: '<host>', | ||
username: '<user>', | ||
@@ -87,5 +87,12 @@ password: '<password>' | ||
Query replacement parameters identified with `?` are replaced with escaped values. Providing a custom format function overrides the built-in escaping with an external library, like [`sqlstring`](https://github.com/mysqljs/sqlstring). | ||
Query replacement parameters identified with `?` are replaced with escaped values. Named replacement parameters are supported with a colon prefix. | ||
```ts | ||
const results1 = await conn.execute('select 1 from dual where 1=?', [42]) | ||
const results2 = await conn.execute('select 1 from dual where 1=:id', { id: 42 }) | ||
``` | ||
Providing a custom format function overrides the built-in escaping with an external library, like [`sqlstring`](https://github.com/mysqljs/sqlstring). | ||
```ts | ||
import { connect } from '@planetscale/database' | ||
@@ -96,3 +103,3 @@ import SqlString from 'sqlstring' | ||
format: SqlString.format, | ||
host: 'aws.connect.psdb.cloud', | ||
host: '<host>', | ||
username: '<user>', | ||
@@ -107,6 +114,24 @@ password: '<password>' | ||
Named replacement parameters are supported with a colon prefix. | ||
### Custom type casting function | ||
Column values are converted to their corresponding JavaScript data types. This can be customized by providing a `cast` function. | ||
```ts | ||
const results = await conn.execute('select 1 from dual where 1=:id', { id: 42 }) | ||
import { connect, cast } from '@planetscale/database' | ||
function inflate(field, value) { | ||
if (field.type === 'INT64' || field.type === 'UINT64') { | ||
return BigInt(value) | ||
} | ||
return cast(field, value) | ||
} | ||
const config = { | ||
cast: inflate, | ||
host: '<host>', | ||
username: '<user>', | ||
password: '<password>' | ||
} | ||
const conn = connect(config) | ||
``` | ||
@@ -113,0 +138,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
26740
322
142