Files
LingvAI/pages/api/graphql.ts

177 lines
5.0 KiB
TypeScript

import { ApolloServer, gql, IResolvers, ApolloError, UserInputError } from "apollo-server-micro";
import { NextApiHandler } from "next";
import NextCors from "nextjs-cors";
import {
getTranslationInfo,
getTranslationText,
getAudio,
replaceExceptedCode,
isValidCode,
LanguageType,
languageList,
LangCode
} from "lingva-scraper";
export const typeDefs = gql`
enum LangType {
SOURCE,
TARGET
}
type Query {
translation(source: String="auto" target: String="en" query: String!): Translation!
audio(lang: String! query: String!): AudioEntry!
languages(type: LangType): [Language]!
}
type Translation {
source: SourceEntry!
target: TargetEntry!
}
type SourceEntry {
lang: Language!
text: String!
audio: [Int]!
detected: Language
typo: String
pronunciation: String
definitions: [DefinitionsGroup]
examples: [String]
similar: [String]
}
type TargetEntry {
lang: Language!
text: String!
audio: [Int]!
pronunciation: String
extraTranslations: [ExtraTranslationsGroup]
}
type AudioEntry {
lang: Language!
text: String!
audio: [Int]!
}
type Language {
code: String!
name: String!
}
type DefinitionsGroup {
type: String!
list: [DefinitionList]!
}
type DefinitionList {
definition: String!
example: String!
field: String
synonyms: [String]
}
type ExtraTranslationsGroup {
type: String!
list: [ExtraTranslationList]!
}
type ExtraTranslationList {
word: String!
article: String
frequency: Int!
meanings: [String]
}
`;
export const resolvers: IResolvers = {
Query: {
async translation(_, args) {
const { source, target, query } = args;
if (!isValidCode(source, LanguageType.SOURCE) || !isValidCode(target, LanguageType.TARGET))
throw new UserInputError("Invalid language code");
const translation = await getTranslationText(source, target, query);
if (!translation)
throw new ApolloError("An error occurred while retrieving the translation");
const info = await getTranslationInfo(source, target, query);
return {
source: {
lang: {
code: source
},
text: query,
detected: info?.detectedSource && {
code: info.detectedSource
},
typo: info?.typo,
pronunciation: info?.pronunciation.query,
definitions: info?.definitions,
examples: info?.examples,
similar: info?.similar
},
target: {
lang: {
code: target
},
text: translation,
pronunciation: info?.pronunciation.translation,
extraTranslations: info?.extraTranslations
}
};
},
audio(_, args) {
const { lang, query } = args;
if (!isValidCode(lang))
throw new UserInputError("Invalid language code");
return {
lang: {
code: lang
},
text: query
};
},
languages(_, args) {
const { type } = args;
const lowerType = type?.toLocaleLowerCase() as typeof LanguageType[keyof typeof LanguageType] | undefined;
const langEntries = Object.entries(languageList[lowerType ?? "all"]);
return langEntries.map(([code, name]) => ({ code, name }));
}
},
...(["SourceEntry", "TargetEntry", "AudioEntry"].reduce((acc, key) => ({
...acc,
[key]: {
async audio(parent) {
const { lang, text } = parent;
const parsedLang = replaceExceptedCode(LanguageType.TARGET, lang.code);
const audio = await getAudio(parsedLang, text);
if (!audio)
throw new ApolloError("An error occurred while retrieving the audio");
return audio;
}
}
}), {} as IResolvers)),
Language: {
name(parent) {
const { code, name } = parent;
return name || languageList.all[code as LangCode];
}
}
};
export const config = {
api: {
bodyParser: false
}
};
const apolloHandler = new ApolloServer({ typeDefs, resolvers }).createHandler({ path: "/api/graphql" });
const handler: NextApiHandler = async (req, res) => {
await NextCors(req, res, {
methods: ["GET", "POST"],
origin: "*"
});
if (req.method !== "OPTIONS")
return apolloHandler(req, res);
res.end();
};
export default handler;