I just want to do a simple thing: import a JSON file and read it within a function to return some specific data of it, but it's been really hard to do.
When on dev env, it works perfectly, but when I try to do the build, the file is not fount. It's like it's not being imported. Since I'm new with Next.JS architecture, I'm not able to find a solution by myself (read a lot of pages with help, but no success).
structure
- root
- config
- labels
data.json
index.ts
index.ts
import data from './data.json';
export type HomeLabels = typeof data.in.home;
export function getHomeLabels(language: string): HomeLabels {
return data[language as keyof typeof data].home;
}
data.json
{
"in":{
"home":{
"contact_button": "Contact",
"view_all_button": "View all"
}
},
"pt-br":{
"home":{
"contact_button": "Contato",
"view_all_button": "Ver tudo"
}
}
}
error on build
TypeError: Cannot read properties of undefined (reading 'home')
at getHomeLabels (/home/runner/work/boutme/boutme/.next/server/chunks/261.js:42:43)
All the solutions that I found out was related to using API or getStaticsProps and using on component. But I really don't want it. I think that it's possible to do what I want (import json and read just on a function), but it's been hard.
I'm trying to return a json file as a controller response, but I can't get the content of the json.
import { Controller, Get, Res, HttpStatus, Query } from '#nestjs/common';
import { Response } from 'express';
import * as MOCKED_RESPONSE_TS from './data/payment-method.data'; // this ts file is imported fine
const MOCKED_RESPONSE = require('./data/payment-method-mock'); // this json file is not found
#Controller('commons')
export class CommonController {
#Get('/payment-method')
getPaymentMoethod(#Res() res: Response): any {
res.status(HttpStatus.OK).send(MOCKED_RESPONSE);
}
}
Actually the log returns: Error: Cannot find module './data/payment-method' and the app doesn't compile
I have done this with express (even with typescript) and works fine.
I don't know if i have to setup my project to read jsons (I'm newby on nest). By the moment I have created a typescript file exporting a const with the json content and I called it successfuly
I guess the problem lies in the way you import your .json file (change import instead of const)
Another advice or solution would be to leverage the .json() method of the res object (which is actually the express adapter response object).
Let's try with this code:
Your common.controller.ts file:
import { Controller, Get, Res, HttpStatus, Query } from '#nestjs/common';
import { Response } from 'express';
import * as MOCKED_RESPONSE_TS from './data/payment-method.data'; // this ts file should still be imported fine
import * as MOCKED_RESPONSE from './data/payment-method-mock.json'; // or use const inside the controller function
#Controller('commons')
export class CommonController {
#Get('/payment-method')
getPaymentMoethod(#Res() res: Response): any {
res.status(HttpStatus.OK).json(MOCKED_RESPONSE); // <= this sends response data as json
}
}
Also in your tsconfig.json file, don't forget to add this line:
tsconfig.json
{
"compilerOptions": {
// ... other options
"resolveJsonModule": true, // here is the important line, this will help VSCode to autocomplete and suggest quick-fixes
// ... other options
}
Last thoughts: you could use the sendfile() method of the res object depending on whether you want to send back a json file or the content of your json file.
Let me know if it helps ;)
first make sure you are calling it correctly.
Are you getting any response at all? if not double check your method name since its spelled like this: getPaymentMoethod and it should be this: getPaymentMethod.
Secondly I would recommend requiring outside the method and setting it to a constant.
Lastly try wrapping it in JSON.stringify() to convert the response to a json stringified object
I have a JSON translation file "./countries/es.json" in my Angular application that I want to import and loop through.
{
...
"Solomon Islands": "Islas Salomón",
"South Sudan": "Sudán del Sur",
...
}
I have "resolveJsonModule": true, in my tsconfig.json file, so in general import goes well.
import * as countries from './countries/es.json';
and if I access the object say countries['Solomon Islands'] I get 'Islas Salomón' that is correct.
However if want to enumerate all countries:
const countries_keys = Object.keys(countries);
countries_keys is an array with one value 'default'. In order to obtain the country list I need to do:
const countries_keys = Object.keys(countries['default']);
My question - is there a way to do the import cleanly so I get the object exactly as in JSON file?
If I had the source file like:
{
countries: {
...
"Solomon Islands": "Islas Salomón",
"South Sudan": "Sudán del Sur",
...
}
}
I could simply do:
import { countries } from './countries/es.json';
but is there a clean way to import my original file as equivalent JSON object without the additional 'default' property.
You need add allowSyntheticDefaultImports in your tsconfig.json.
tsconfig.json
{
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true
}
TS
import countries from './countries/es.json';
import is not a good idea here later on if you want to move your file to some server you will need to rewrite the whole logic i would suggest to use httpclient get call here.So move you file to assets folder and then
constructor(private http:HttpClient){
this.http.get('assets/yourfilepath').subscribe(data=>{
const countries_keys = Object.keys(data['countries']);
console.log(data['countries'])//data is your json object here
})
}
What worked form me was:
import countries from './countries/es.json';
This is the "default import"
I have the following json file containing a configuration:
{
"config1": { //this would be a map
"a": [ "string1", "string2"],
"b": [ "string1", "string2"]
}
}
Prior to migrating to typescript the following worked:
import * as myConfig from './config.json';
...
myConfig.config1[this.state.section]; //section would be either "a" or "b"
Now the code gives me:
Element implicitely has an "any" type because blablabla has no index signature
Note that I made sure that the following are set in my tsconfig file:
"esModuleInterop": true,
"resolveJsonModule": true,
But it still doesn't work.
What am I missing?
Edit:
It seems has something to do with runtime validation, the following seems fine:
myConfig.config1["a"]; //alright!
but this is not:
const name:string = "a";
myConfig.config1[name]; //NOT!
regardless I need to pass a variable..how can I do that? :(
You don't need a asterisk any more. Starting Typescript 2.9 (I can be wrons about specific version) you can do just import myConfig from './config.json';
If you still have this issue I guess there is no typedef for your config file so typescript transpiler can't get what is the structure of your imported object.
create #types directory inside src folder of your project
create myConfig.d.ts inside of #types
define the structure of your config file in myConfig.d.ts like:
declare module "myConfig.json" {
const config: object;
export default config;
}
In order to make "bracket notation" working your config type should define an index signature like:
interface IConfig {
[ key: string ]: object;
}
You can read more about it https://www.typescriptlang.org/docs/handbook/interfaces.html#indexable-types
I have a JSON file that looks like following:
{
"primaryBright": "#2DC6FB",
"primaryMain": "#05B4F0",
"primaryDarker": "#04A1D7",
"primaryDarkest": "#048FBE",
"secondaryBright": "#4CD2C0",
"secondaryMain": "#00BFA5",
"secondaryDarker": "#009884",
"secondaryDarkest": "#007F6E",
"tertiaryMain": "#FA555A",
"tertiaryDarker": "#F93C42",
"tertiaryDarkest": "#F9232A",
"darkGrey": "#333333",
"lightGrey": "#777777"
}
I'm trying to import it into a .tsx file. For this I added this to the type definition:
declare module "*.json" {
const value: any;
export default value;
}
And I'm importing it like this.
import colors = require('../colors.json')
And in the file, I use the color primaryMain as colors.primaryMain. However I get an error:
Property 'primaryMain' does not exist on type 'typeof "*.json"
With TypeScript 2.9.+ you can simply import JSON files with benefits like typesafety and intellisense by doing this:
import colorsJson from '../colors.json'; // This import style requires "esModuleInterop", see "side notes"
console.log(colorsJson.primaryBright);
Make sure to add these settings in the compilerOptions section of your tsconfig.json (documentation):
"resolveJsonModule": true,
"esModuleInterop": true,
Side notes:
Typescript 2.9.0 has a bug with this JSON feature, it was fixed with 2.9.2
The esModuleInterop is only necessary for the default import of the colorsJson. If you leave it set to false then you have to import it with import * as colorsJson from '../colors.json'
The import form and the module declaration need to agree about the shape of the module, about what it exports.
When you write (a suboptimal practice for importing JSON since TypeScript 2.9 when targeting compatible module formatssee note)
declare module "*.json" {
const value: any;
export default value;
}
You are stating that all modules that have a specifier ending in .json have a single export named default.
There are several ways you can correctly consume such a module including
import a from "a.json";
a.primaryMain
and
import * as a from "a.json";
a.default.primaryMain
and
import {default as a} from "a.json";
a.primaryMain
and
import a = require("a.json");
a.default.primaryMain
The first form is the best and the syntactic sugar it leverages is the very reason JavaScript has default exports.
However I mentioned the other forms to give you a hint about what's going wrong. Pay special attention to the last one. require gives you an object representing the module itself and not its exported bindings.
So why the error? Because you wrote
import a = require("a.json");
a.primaryMain
And yet there is no export named primaryMain declared by your "*.json".
All of this assumes that your module loader is providing the JSON as the default export as suggested by your original declaration.
Note: Since TypeScript 2.9, you can use the --resolveJsonModule compiler flag to have TypeScript analyze imported .json files and provide correct information regarding their shape obviating the need for a wildcard module declaration and validating the presence of the file. This is not supported for certain target module formats.
Here's how to import a json file at runtime
import fs from 'fs'
var dataArray = JSON.parse(fs.readFileSync('data.json', 'utf-8'))
This way you avoid issues with tsc slowing down or running out of memory when importing large files, which can happen when using resolveJsonModule.
It's easy to use typescript version 2.9+. So you can easily import JSON files as #kentor decribed.
But if you need to use older versions:
You can access JSON files in more TypeScript way. First, make sure your new typings.d.ts location is the same as with the include property in your tsconfig.json file.
If you don't have an include property in your tsconfig.json file. Then your folder structure should be like that:
- app.ts
+ node_modules/
- package.json
- tsconfig.json
- typings.d.ts
But if you have an include property in your tsconfig.json:
{
"compilerOptions": {
},
"exclude" : [
"node_modules",
"**/*spec.ts"
], "include" : [
"src/**/*"
]
}
Then your typings.d.ts should be in the src directory as described in include property
+ node_modules/
- package.json
- tsconfig.json
- src/
- app.ts
- typings.d.ts
As In many of the response, You can define a global declaration for all your JSON files.
declare module '*.json' {
const value: any;
export default value;
}
but I prefer a more typed version of this. For instance, let's say you have configuration file config.json like that:
{
"address": "127.0.0.1",
"port" : 8080
}
Then we can declare a specific type for it:
declare module 'config.json' {
export const address: string;
export const port: number;
}
It's easy to import in your typescript files:
import * as Config from 'config.json';
export class SomeClass {
public someMethod: void {
console.log(Config.address);
console.log(Config.port);
}
}
But in compilation phase, you should copy JSON files to your dist folder manually. I just add a script property to my package.json configuration:
{
"name" : "some project",
"scripts": {
"build": "rm -rf dist && tsc && cp src/config.json dist/"
}
}
In my case I needed to change tsconfig.node.json:
{
"compilerOptions": {
...
"resolveJsonModule": true
},
"include": [..., "colors.json"]
}
And to import like that:
import * as colors from './colors.json'
Or like that:
import colors from './colors.json'
with "esModuleInterop": true
You should add
"resolveJsonModule": true
as part of compilerOptions to tsconfig.json.
Often in Node.js applications a .json is needed. With TypeScript 2.9, --resolveJsonModule allows for importing, extracting types from and generating .json files.
Example #
// tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"resolveJsonModule": true,
"esModuleInterop": true
}
}
// .ts
import settings from "./settings.json";
settings.debug === true; // OK
settings.dry === 2; // Error: Operator '===' cannot be applied boolean and number
// settings.json
{
"repo": "TypeScript",
"dry": false,
"debug": false
}
by: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html
Another way to go
const data: {[key: string]: any} = require('./data.json');
This was you still can define json type is you want and don't have to use wildcard.
For example, custom type json.
interface User {
firstName: string;
lastName: string;
birthday: Date;
}
const user: User = require('./user.json');
In an Angular (typescript) app, I needed to include a .json file in my environment.ts. To do so, I had to set two options in tsconfig:
{
"compilerOptions": {
"moduleResolution": "node",
"resolveJsonModule": true
}
}
Then, I could import my json file into the environment.ts:
import { default as someObjectName } from "../some-json-file.json";
You can import a JSON file without modifying tsconfig you tell explicitly that you are importing JSON
import mydata from './mydataonfile.json' assert { type: "json" };
I know this does not fully answer the question but many people come here to know how to load JSON directly from a file.
Enable "resolveJsonModule": true in tsconfig.json file and implement as below code, it's work for me:
const config = require('./config.json');
Note that if you using #kentor ways
Make sure to add these settings in the compilerOptions section of your tsconfig.json (documentation):
You need to add --resolveJsonModule and--esModuleInterop behind tsc command to compile your TypeScript file.
Example:
tsc --resolveJsonModule --esModuleInterop main.ts
require is a common way to load a JSON file in Node.js
in my case I had to change: "include": ["src"] to "include": ["."] in addition to "resolveJsonModule":true because I tried to import manifest.json from the root of the project and not from ./src