How to convert json to sql test fixture insert statements
- Step 1Write the fixture as a JSON array — Define your fixture rows as
[{...},{...}]— one object per row. A single object also works (one-row fixture). Factory output that nests rows under a key needs json-path-extractor ($.fixtures[*]) to pull the array out first. - Step 2Drop the fixture file on the converter — Drag the
.jsonfile onto the dropzone..xlsx/.xls/.odsare accepted too (first sheet → JSON array). A raw.csvfixture must go through csv-to-json first. - Step 3Name the table and choose the test DB dialect — Enter the table the test inserts into (e.g.
users) and select PostgreSQL, MySQL, or SQLite to match your test database. SQLite is common for fast in-memory test DBs; pick the dialect your test harness actually runs. - Step 4Exclude DB-assigned columns — List columns the database fills itself —
created_at,updated_at, or an auto-incrementid— in Exclude columns so the INSERT only sets what your fixture should set. Comma-separated, e.g.created_at, updated_at. - Step 5Enable idempotent inserts for repeatable suites — Turn on ON CONFLICT DO NOTHING so re-running the fixture between tests skips rows that already exist instead of erroring. Keep Include CREATE TABLE on if the test sets up its own schema, off if migrations already created the tables.
- Step 6Generate and wire into your test setup — Click Generate SQL, then Copy or Download SQL. Paste into a Jest
beforeEach/db.exec(), a PHPUnit dataset, or a pytest fixture file. Pair it with your ownTRUNCATE/rollback teardown. The stats line confirms the row and statement count.
Fixture options and behaviour by dialect
How the conflict toggle and quoting differ per dialect — the part that matters most for repeatable test seeds.
| Aspect | PostgreSQL | MySQL | SQLite |
|---|---|---|---|
| Identifier quoting | "double" | backtick | "double" |
| Boolean fixture value | BOOLEAN / TRUE,FALSE | TINYINT(1) / 1,0 | BOOLEAN / TRUE,FALSE |
| ON CONFLICT DO NOTHING | appends ON CONFLICT DO NOTHING | switches to INSERT IGNORE INTO | appends ON CONFLICT DO NOTHING |
| Decimal fixture type | DOUBLE PRECISION | REAL | REAL |
What the converter does and doesn't do for fixtures
Grounded in the real implementation so your test setup assumes only what the tool actually generates.
| Need | Supported here? | How / alternative |
|---|---|---|
| Multiple tables in one run | No | One run per table; concatenate outputs in FK order |
| Partial fixture rows | Yes | Key union; missing keys → NULL |
| Idempotent re-seed | Yes | ON CONFLICT DO NOTHING / INSERT IGNORE |
| Teardown (TRUNCATE/DELETE) | No | Handle in your test's afterEach/rollback |
| Upsert (update on conflict) | No | Only skip-on-conflict; edit by hand for upsert |
| Nested object fixture field | Stored as TEXT | json-flattener for flat columns |
| PRIMARY KEY / constraints | No | Add to DDL, or use INSERT-only against a migrated table |
Free vs Pro limits
JSON to SQL is a Pro tool; limits come from the json family in the tier table.
| Limit | Free | Pro |
|---|---|---|
| Max fixture file size | 2 MB | 100 MB |
| Files per run | 1 | 10 |
| Rows per INSERT batch | 100 (fixed) | 100 (fixed) |
Cookbook
JSON fixtures in, test-setup SQL out — copied from the converter so the conflict handling, partial rows, and quoting are exactly what your harness will run.
User fixture for a Jest database test
ExampleA two-row user fixture for a Postgres test DB. Booleans become BOOLEAN, the apostrophe in the name is escaped — ready to pass to db.exec() in beforeEach.
Input (fixtures/users.json):
[
{"id": 1, "email": "ada@x.com", "is_admin": true},
{"id": 2, "email": "o'neil@x.com", "is_admin": false}
]
Output (PostgreSQL):
CREATE TABLE IF NOT EXISTS "users" (
"id" INTEGER,
"email" TEXT,
"is_admin" BOOLEAN
);
INSERT INTO "users" ("id", "email", "is_admin")
VALUES
(1, 'ada@x.com', TRUE),
(2, 'o''neil@x.com', FALSE);Idempotent fixture re-seed between tests
ExampleLoading the same fixture before every test would fail on duplicate keys. Enable the conflict toggle so re-seeds skip existing rows instead of erroring.
Config: ON CONFLICT DO NOTHING enabled (PostgreSQL)
Output:
INSERT INTO "roles" ("id", "name")
VALUES
(1, 'admin'),
(2, 'user') ON CONFLICT DO NOTHING;
MySQL equivalent (same toggle, MySQL dialect):
INSERT IGNORE INTO `roles` (`id`, `name`)
VALUES
(1, 'admin'),
(2, 'user');Minimal partial fixtures with NULL fill
ExampleTest only cares about email and status. Omitting other fields in some rows yields NULL — no need to specify every column in every fixture row.
Input:
[
{"email": "a@x.com", "status": "active", "plan": "pro"},
{"email": "b@x.com", "status": "active"}
]
Output (PostgreSQL):
CREATE TABLE IF NOT EXISTS "users" (
"email" TEXT,
"status" TEXT,
"plan" TEXT
);
INSERT INTO "users" ("email", "status", "plan")
VALUES
('a@x.com', 'active', 'pro'),
('b@x.com', 'active', NULL);Excluding DB-assigned columns from the fixture
ExampleThe database fills id and created_at itself. Exclude them so the INSERT only sets fixture-controlled columns and doesn't fight the schema defaults.
Input:
[{"id": 1, "email": "a@x.com", "created_at": "2026-01-01"}]
Config: Exclude columns = id, created_at, dialect = SQLite
Output (SQLite):
CREATE TABLE IF NOT EXISTS "users" (
"email" TEXT
);
INSERT INTO "users" ("email")
VALUES
('a@x.com');
-- id (AUTOINCREMENT) and created_at (DEFAULT now) are left
-- to the database.INSERT-only fixtures against a migrated schema
ExampleWhen migrations already created the tables with real constraints, turn off Include CREATE TABLE so you load only the fixture rows.
Config: Include CREATE TABLE = off, dialect = MySQL
Output:
INSERT INTO `products` (`sku`, `price`)
VALUES
('A1', 9.99),
('B2', 14.5);
-- No CREATE TABLE: your migrated schema, keys, and
-- foreign keys are untouched. Wrap in a transaction in
-- your test and roll back in teardown.Errors and edge cases
Real errors and silent failures sourced from each platform's own documentation. Match the wording to the row, fix what the row says to fix.
Only one table per run
By designThe whole fixture array goes into the single table named in Table name. There's no multi-table fixture support in one pass. For a fixture spanning users + orders + line_items, split the JSON by table, run the converter once per table, and concatenate the outputs in foreign-key dependency order before loading.
No teardown / TRUNCATE generated
By designThe tool only generates inserts (and optionally CREATE TABLE). It never emits TRUNCATE, DELETE, or DROP. Handle teardown in your test framework — a transaction-per-test with rollback, or an explicit afterEach truncate — to keep tests isolated.
Conflict toggle skips, doesn't update
By designON CONFLICT DO NOTHING / INSERT IGNORE skips conflicting rows; it does not update them. If a test expects a fixture row to overwrite an existing one, this won't do it — there's no upsert. Truncate first, or hand-edit the SQL into an upsert form for your DB.
Nested fixture object becomes TEXT
By designA fixture field holding an object or array is JSON-stringified into a TEXT column, not separate columns. If your assertion reads row.settings.theme, store the JSON and parse it in the test, or flatten the fixture with json-flattener so settings_theme is its own column.
Excluded auto-id still referenced by FK fixtures
CautionIf you exclude an auto-increment id so the DB assigns it, related fixtures that reference that id by a hardcoded value may not match the DB-assigned one. Either keep explicit ids in the fixtures (don't exclude id), or insert and capture generated ids in the test before seeding dependents.
Boolean differs by dialect (TRUE/FALSE vs 1/0)
ExpectedPostgres/SQLite emit BOOLEAN with TRUE/FALSE; MySQL emits TINYINT(1) with 1/0. If a test asserts the literal value, match it to the dialect you generated. Switching dialects changes both the column type and the boolean literal.
Raw CSV fixture dropped
Invalid inputOnly .json and spreadsheet files (.xlsx/.xls/.ods) become rows. A .csv fixture fails JSON.parse. Convert it with csv-to-json first, then generate the fixture SQL here.
Empty fixture array
ExpectedAn empty [] produces -- Empty array, no statements generated rather than an error. If your fixture loader runs that, it's a no-op — useful as a deliberately-empty fixture, but check the stats row count if you expected data.
Apostrophes and quotes in fixture strings
SupportedSingle quotes in fixture values are escaped by doubling ('o''neil@x.com'), so realistic names and emails with apostrophes load cleanly. No backslash escaping is used, matching standard SQL string literals across all three dialects.
Frequently asked questions
How do I load the generated SQL in a Jest test?
Generate the fixture SQL, then run it in a beforeEach via your DB client — pool.query(sql) for pg, or db.exec(sql) for better-sqlite3. Enable ON CONFLICT DO NOTHING so repeated beforeEach runs don't fail on existing rows, and pair it with a transaction rollback or truncate in afterEach for isolation.
Can I generate fixtures for several tables at once?
No — one run targets one table. Split your fixture JSON by table, run the converter once per table (changing the Table name each time), and concatenate the resulting .sql files in foreign-key dependency order (parents before children) before loading them in your test setup.
Does it generate teardown / TRUNCATE statements?
No. It only emits INSERTs and, optionally, CREATE TABLE. Teardown is yours to manage — the cleanest pattern is wrapping each test in a transaction and rolling back, which removes the fixture rows without any explicit DELETE. Alternatively, TRUNCATE the tables in afterEach.
How do I make the fixture safe to load repeatedly?
Turn on ON CONFLICT DO NOTHING. In Postgres/SQLite it appends a table-wide ON CONFLICT DO NOTHING; in MySQL it switches to INSERT IGNORE INTO. Both skip rows that already exist (you need a PRIMARY KEY/UNIQUE on the table for that), so a fixture loaded before every test won't fail on duplicate keys.
Can it do an upsert so a fixture overwrites existing data?
No. The only conflict behaviour is skip-on-conflict — it never updates existing rows. If a test needs the fixture to overwrite, truncate the table first, or hand-edit the generated SQL into your DB's upsert form (ON CONFLICT ... DO UPDATE or ON DUPLICATE KEY UPDATE).
How do I omit auto-generated columns like created_at or id?
List them in Exclude columns (comma-separated, e.g. id, created_at). They're dropped from the CREATE TABLE and every INSERT, so the database's defaults/auto-increment fill them. If other fixtures reference an excluded auto-id, keep explicit ids instead so foreign keys line up.
What happens to nested objects in a fixture?
They're JSON-stringified into a TEXT column. If a test asserts on a nested value, either parse the stored JSON in the test or flatten the fixture first with json-flattener so each nested key becomes its own column before conversion.
Do partial fixture rows work?
Yes. The column set is the union of all keys across rows, and any row missing a key gets NULL. So you can write minimal fixtures that only set the fields a given test cares about, without padding every row to the full schema.
Which dialect should I pick for fast test databases?
Match your test harness. SQLite (often in-memory) is popular for fast unit-level DB tests; pick PostgreSQL or MySQL if your integration tests run against a real instance via testcontainers or a CI service. The boolean literals and quoting differ by dialect, so generate for the one your tests actually run.
Does it create a PRIMARY KEY for the fixture table?
No. The generated CREATE TABLE has columns and types only. For ON CONFLICT DO NOTHING / INSERT IGNORE to actually skip duplicates, the table needs a PRIMARY KEY or UNIQUE constraint — so either add one to the DDL by hand, or run INSERT-only output (Include CREATE TABLE off) against tables your migrations already created.
Is fixture data uploaded anywhere?
No. Conversion runs entirely in your browser. Fixture files — even realistic sample data — never reach a server; only an anonymous run counter (no content) is recorded for dashboard stats. That keeps CI/test data hygiene fully on your side.
How many fixture rows per INSERT statement?
100 rows per multi-row INSERT ... VALUES, a fixed batch size with no UI control. Most fixtures are small enough for a single statement; a 300-row fixture yields three INSERTs plus the CREATE TABLE. The stats line reports the count.
Privacy first
Conversion runs locally in your browser. No file is uploaded — only metadata counters are saved for signed-in dashboard stats.